This commit is contained in:
wty-yy
2025-12-27 17:42:30 +08:00
parent 0a3985445a
commit f92f34ac14
28 changed files with 452 additions and 22 deletions

20
CMD.md
View File

@@ -15,6 +15,20 @@ python robogauge/scripts/run.py \
--num-processes 5 \ --num-processes 5 \
--seeds 0 1 2 3 4 \ --seeds 0 1 2 3 4 \
--frictions 0.5 1.0 1.5 2.0 2.5 \ --frictions 0.5 1.0 1.5 2.0 2.5 \
--compress-logs \
--headless
# Terrain with Level, need specify goals (default is target_pos)
python robogauge/scripts/run.py \
--task go2_moe.slope \
--experiment-name debug \
--multi \
--num-processes 5 \
--seeds 0 1 2 3 4 \
--frictions 1.0 \
--level 3 \
--goals max_velocity diagonal_velocity\
--compress-logs \
--headless --headless
``` ```
@@ -25,8 +39,9 @@ python robogauge/scripts/run.py \
--task go2_moe.slope \ --task go2_moe.slope \
--experiment-name debug \ --experiment-name debug \
--search-max-level \ --search-max-level \
--seeds 0 1 2 \ --seeds 0 1 2 3 4 \
--frictions 1.0 \ --frictions 1.0 \
--compress-logs \
--headless --headless
``` ```
@@ -39,8 +54,9 @@ python robogauge/scripts/run.py \
--stress-terrain-names flat slope stairs_up stairs_down wave \ --stress-terrain-names flat slope stairs_up stairs_down wave \
--stress-num-processes 2 \ --stress-num-processes 2 \
--num-processes 3 \ --num-processes 3 \
--seeds 0 1 2 \ --seeds 0 1 2 3 4 \
--frictions 0.5 1.0 1.5 2.0 2.5 \ --frictions 0.5 1.0 1.5 2.0 2.5 \
--compress-logs \
--headless --headless
``` ```

View File

@@ -12,6 +12,14 @@
| - | - | - | | - | - | - |
| Unitree go2 | 四足 | ✅ | | Unitree go2 | 四足 | ✅ |
## 安装
参考[PyTorch官网](https://pytorch.org/get-started/locally/)安装2.0以上任意版本, 安装本仓库
```bash
git clone https://github.com/wty-yy/RoboGauge.git
pip install -e .
```
## 目录结构 ## 目录结构
- `robogauge/scripts`: 启动指标测试程序 - `robogauge/scripts`: 启动指标测试程序
- `robogauge/tasks`: 定义测试任务 - `robogauge/tasks`: 定义测试任务

View File

@@ -1,4 +1,12 @@
# UPDATE # UPDATE
## 20251227
### v0.1.17
1. args中添加`goals`配置, 在LevelPipeline和MultiPipeline中指定goals, 修正压力测试中评估的目标不对的问题
2. 评估必须使用5个种子, 否则LevelPipeline中的80%通过率就没有意义了, 并且提高5个种子最终的评级影响可能非常大 (moe.stairs_up Friction 2.0从Lv 7->10)
3. 在StressPipeline测试中, 不再保存
4. 添加obstacles地形
5. 优化分数计算方法, 按照`(level-1)*0.1+metric_score`计算
6. 加入日志文件夹递归保存, StressPipeline -> LevelPipeline -> MultiPipeline, 加入压缩参数`--compress-logs`, 将日志文件夹压缩为`subtasks.tar.xz`, 日志文件压缩`4M->100kb`
## 20251226 ## 20251226
### v0.1.16 ### v0.1.16
1. 基本完成StressPipeline, 加入绘制进度条的线程, 其他进程通过Queue更新主进程的进度条 1. 基本完成StressPipeline, 加入绘制进度条的线程, 其他进程通过Queue更新主进程的进度条

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_1">
<!-- Terrain Type: obstacle, Level: 1, Obstacle height: 0.0730, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.036500000000000005" type="box" size="0.5 5.0 0.036500000000000005" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.036500000000000005" type="box" size="0.5 5.0 0.036500000000000005" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.036500000000000005" type="box" size="0.5 5.0 0.036500000000000005" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.036500000000000005" type="box" size="0.5 5.0 0.036500000000000005" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.036500000000000005" type="box" size="0.5 5.0 0.036500000000000005" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_10">
<!-- Terrain Type: obstacle, Level: 10, Obstacle height: 0.2800, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.14" type="box" size="0.5 5.0 0.14" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.14" type="box" size="0.5 5.0 0.14" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.14" type="box" size="0.5 5.0 0.14" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.14" type="box" size="0.5 5.0 0.14" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.14" type="box" size="0.5 5.0 0.14" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_2">
<!-- Terrain Type: obstacle, Level: 2, Obstacle height: 0.0960, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.048" type="box" size="0.5 5.0 0.048" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.048" type="box" size="0.5 5.0 0.048" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.048" type="box" size="0.5 5.0 0.048" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.048" type="box" size="0.5 5.0 0.048" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.048" type="box" size="0.5 5.0 0.048" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_3">
<!-- Terrain Type: obstacle, Level: 3, Obstacle height: 0.1190, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.059500000000000004" type="box" size="0.5 5.0 0.059500000000000004" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.059500000000000004" type="box" size="0.5 5.0 0.059500000000000004" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.059500000000000004" type="box" size="0.5 5.0 0.059500000000000004" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.059500000000000004" type="box" size="0.5 5.0 0.059500000000000004" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.059500000000000004" type="box" size="0.5 5.0 0.059500000000000004" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_4">
<!-- Terrain Type: obstacle, Level: 4, Obstacle height: 0.1420, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.07100000000000001" type="box" size="0.5 5.0 0.07100000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.07100000000000001" type="box" size="0.5 5.0 0.07100000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.07100000000000001" type="box" size="0.5 5.0 0.07100000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.07100000000000001" type="box" size="0.5 5.0 0.07100000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.07100000000000001" type="box" size="0.5 5.0 0.07100000000000001" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_5">
<!-- Terrain Type: obstacle, Level: 5, Obstacle height: 0.1650, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.0825" type="box" size="0.5 5.0 0.0825" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.0825" type="box" size="0.5 5.0 0.0825" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.0825" type="box" size="0.5 5.0 0.0825" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.0825" type="box" size="0.5 5.0 0.0825" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.0825" type="box" size="0.5 5.0 0.0825" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_6">
<!-- Terrain Type: obstacle, Level: 6, Obstacle height: 0.1880, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.094" type="box" size="0.5 5.0 0.094" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.094" type="box" size="0.5 5.0 0.094" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.094" type="box" size="0.5 5.0 0.094" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.094" type="box" size="0.5 5.0 0.094" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.094" type="box" size="0.5 5.0 0.094" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_7">
<!-- Terrain Type: obstacle, Level: 7, Obstacle height: 0.2110, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.10550000000000001" type="box" size="0.5 5.0 0.10550000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.10550000000000001" type="box" size="0.5 5.0 0.10550000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.10550000000000001" type="box" size="0.5 5.0 0.10550000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.10550000000000001" type="box" size="0.5 5.0 0.10550000000000001" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.10550000000000001" type="box" size="0.5 5.0 0.10550000000000001" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_8">
<!-- Terrain Type: obstacle, Level: 8, Obstacle height: 0.2340, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.11700000000000002" type="box" size="0.5 5.0 0.11700000000000002" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.11700000000000002" type="box" size="0.5 5.0 0.11700000000000002" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.11700000000000002" type="box" size="0.5 5.0 0.11700000000000002" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.11700000000000002" type="box" size="0.5 5.0 0.11700000000000002" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.11700000000000002" type="box" size="0.5 5.0 0.11700000000000002" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -0,0 +1,23 @@
<mujoco model="obstacle_9">
<!-- Terrain Type: obstacle, Level: 9, Obstacle height: 0.2570, length: 1.0000, gap: 1.0000 -->
<statistic center="0 0 0.1" extent="0.8" />
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0" />
<rgba haze="0.15 0.25 0.35 1" />
<global azimuth="-130" elevation="-20" />
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072" />
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3" markrgb="0.8 0.8 0.8" width="300" height="300" />
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.0" />
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true" />
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" />
<geom pos="1.5 0.0 0.1285" type="box" size="0.5 5.0 0.1285" quat="1.0 0.0 0.0 0.0" />
<geom pos="3.5 0.0 0.1285" type="box" size="0.5 5.0 0.1285" quat="1.0 0.0 0.0 0.0" />
<geom pos="5.5 0.0 0.1285" type="box" size="0.5 5.0 0.1285" quat="1.0 0.0 0.0 0.0" />
<geom pos="7.5 0.0 0.1285" type="box" size="0.5 5.0 0.1285" quat="1.0 0.0 0.0 0.0" />
<geom pos="9.5 0.0 0.1285" type="box" size="0.5 5.0 0.1285" quat="1.0 0.0 0.0 0.0" />
</worldbody>
</mujoco>

View File

@@ -15,6 +15,7 @@ task_register.register('go2.slope', BasePipeline, MujocoConfig, Go2SlopeGaugeCon
task_register.register('go2.wave', BasePipeline, MujocoConfig, Go2WaveGaugeConfig, Go2TerrainConfig) task_register.register('go2.wave', BasePipeline, MujocoConfig, Go2WaveGaugeConfig, Go2TerrainConfig)
task_register.register('go2.stairs_up', BasePipeline, MujocoConfig, Go2StairsUpGaugeConfig, Go2TerrainConfig) task_register.register('go2.stairs_up', BasePipeline, MujocoConfig, Go2StairsUpGaugeConfig, Go2TerrainConfig)
task_register.register('go2.stairs_down', BasePipeline, MujocoConfig, Go2StairsDownGaugeConfig, Go2TerrainConfig) task_register.register('go2.stairs_down', BasePipeline, MujocoConfig, Go2StairsDownGaugeConfig, Go2TerrainConfig)
task_register.register('go2.obstacle', BasePipeline, MujocoConfig, Go2ObstacleGaugeConfig, Go2TerrainConfig)
# Go2 MoE # Go2 MoE
task_register.register('go2_moe.flat', BasePipeline, MujocoConfig, Go2FlatGaugeConfig, Go2MoEConfig) task_register.register('go2_moe.flat', BasePipeline, MujocoConfig, Go2FlatGaugeConfig, Go2MoEConfig)
@@ -22,3 +23,4 @@ task_register.register('go2_moe.slope', BasePipeline, MujocoConfig, Go2SlopeGaug
task_register.register('go2_moe.wave', BasePipeline, MujocoConfig, Go2WaveGaugeConfig, Go2MoETerrainConfig) task_register.register('go2_moe.wave', BasePipeline, MujocoConfig, Go2WaveGaugeConfig, Go2MoETerrainConfig)
task_register.register('go2_moe.stairs_up', BasePipeline, MujocoConfig, Go2StairsUpGaugeConfig, Go2MoETerrainConfig) task_register.register('go2_moe.stairs_up', BasePipeline, MujocoConfig, Go2StairsUpGaugeConfig, Go2MoETerrainConfig)
task_register.register('go2_moe.stairs_down', BasePipeline, MujocoConfig, Go2StairsDownGaugeConfig, Go2MoETerrainConfig) task_register.register('go2_moe.stairs_down', BasePipeline, MujocoConfig, Go2StairsDownGaugeConfig, Go2MoETerrainConfig)
task_register.register('go2_moe.obstacle', BasePipeline, MujocoConfig, Go2ObstacleGaugeConfig, Go2MoETerrainConfig)

View File

@@ -11,3 +11,4 @@ from .go2_slope_task import Go2SlopeGaugeConfig
from .go2_wave_task import Go2WaveGaugeConfig from .go2_wave_task import Go2WaveGaugeConfig
from .go2_stairs_up_task import Go2StairsUpGaugeConfig from .go2_stairs_up_task import Go2StairsUpGaugeConfig
from .go2_stairs_down_task import Go2StairsDownGaugeConfig from .go2_stairs_down_task import Go2StairsDownGaugeConfig
from .go2_obstacle_task import Go2ObstacleGaugeConfig

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
'''
@File : go2_obstacle_task.py
@Time : 2025/12/27 14:32:22
@Author : wty-yy
@Version : 1.0
@Blog : https://wty-yy.github.io/
@Desc : Go2 Obstacle Task Configuration
'''
from robogauge.tasks.robots import Go2Config, Go2MoEConfig
from robogauge.tasks.gauge import ObstacleGaugeConfig
from robogauge.tasks.simulator.mujoco_config import MujocoConfig
class Go2ObstacleGaugeConfig(ObstacleGaugeConfig):
class metrics(ObstacleGaugeConfig.metrics):
class dof_limits(ObstacleGaugeConfig.metrics.dof_limits):
enabled = True
soft_dof_limit_ratio = 0.7
dof_names = ['hip', 'thigh'] # List of DOF names to monitor, None for all

View File

@@ -5,3 +5,4 @@ from .gauge_configs.slope_gauge_config import SlopeGaugeConfig
from .gauge_configs.wave_gauge_config import WaveGaugeConfig from .gauge_configs.wave_gauge_config import WaveGaugeConfig
from .gauge_configs.stairs_up_gauge_config import StairsUpGaugeConfig from .gauge_configs.stairs_up_gauge_config import StairsUpGaugeConfig
from .gauge_configs.stairs_down_gauge_config import StairsDownGaugeConfig from .gauge_configs.stairs_down_gauge_config import StairsDownGaugeConfig
from .gauge_configs.obstacle_gauge_config import ObstacleGaugeConfig

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
'''
@File : obstacle_gauge_config.py
@Time : 2025/12/27 14:33:14
@Author : wty-yy
@Version : 1.0
@Blog : https://wty-yy.github.io/
@Desc : Obstacle Gauge Configuration
'''
from robogauge.tasks.gauge.base_gauge_config import BaseGaugeConfig
class ObstacleGaugeConfig(BaseGaugeConfig):
gauge_class = 'BaseGauge'
class assets(BaseGaugeConfig.assets):
terrain_name = "obstacle"
terrain_level = 10 # 1-10
terrain_xmls = [
'{ROBOGAUGE_ROOT_DIR}/resources/terrains/obstacle/obstacle_10.xml',
'{ROBOGAUGE_ROOT_DIR}/resources/terrains/wall/10x10_wall.xml',
]
terrain_spawn_pos = [0.5, 2.0, 0.0] # x y z [m], robot freejoint spawn position on the terrain
class goals(BaseGaugeConfig.goals):
class target_pos_velocity: # goal to reach a target position by velocity command
enabled = True
target_pos = [6.5, -2.0, 0.3] # x y z [m], target position in the environment, used for target position goal
lin_vel_x = 0.8 # +/- m/s
lin_vel_y = 1.0 # +/- m/s
ang_vel_yaw = 1.5 # +/- rad/s
max_cmd_duration = 30.0 # [s] maximum duration to reach the target position
reach_threshold = 0.1

View File

@@ -87,6 +87,9 @@ class TerrainSearchLevelsConfig(Config):
[1.1, 3.0, 7], [1.1, 3.0, 7],
] ]
class obstacle:
levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
class TerrainEvalLevelsConfig(Config): class TerrainEvalLevelsConfig(Config):
class slope: class slope:
levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

View File

@@ -21,7 +21,7 @@ class WaveGaugeConfig(BaseGaugeConfig):
] ]
terrain_spawn_pos = [1.5, 1.25, 0.0] # x y z [m], robot freejoint spawn position on the terrain terrain_spawn_pos = [1.5, 1.25, 0.0] # x y z [m], robot freejoint spawn position on the terrain
class goals: class goals(BaseGaugeConfig.goals):
class target_pos_velocity: # goal to reach a target position by velocity command class target_pos_velocity: # goal to reach a target position by velocity command
enabled = True enabled = True
target_pos = [6.5, -2.65, 0.8] # x y z [m], target position in the environment, used for target position goal target_pos = [6.5, -2.65, 0.8] # x y z [m], target position in the environment, used for target position goal

View File

@@ -12,6 +12,7 @@ import yaml
from robogauge.tasks.pipeline.multi_pipeline import MultiPipeline from robogauge.tasks.pipeline.multi_pipeline import MultiPipeline
from robogauge.utils.logger import Logger from robogauge.utils.logger import Logger
from robogauge.utils.progress_monitor import report_progress, ProgressTypes, ProgressData from robogauge.utils.progress_monitor import report_progress, ProgressTypes, ProgressData
from robogauge.utils.file_utils import compress_directory
level_logger = Logger() # LevelPipeline logger level_logger = Logger() # LevelPipeline logger
@@ -21,7 +22,11 @@ class LevelPipeline:
self.seeds = args.seeds self.seeds = args.seeds
self.console_output = console_output self.console_output = console_output
self.progress_data = progress_data self.progress_data = progress_data
level_logger.create(args.experiment_name+'_level', args.run_name, console_output=console_output) parent_log_dir = getattr(args, 'parent_log_dir', None)
level_logger.create(args.experiment_name+'_level', args.run_name, console_output=console_output, parent_log_dir=parent_log_dir)
self.args.parent_log_dir = str(level_logger.log_dir / "subtasks")
self.compress_logs = args.compress_logs
args.compress_logs = False # Disable child log compression
def run(self): def run(self):
level_logger.info(f"🚀 Starting Level Searcher for '{self.args.experiment_name}'.") level_logger.info(f"🚀 Starting Level Searcher for '{self.args.experiment_name}'.")
@@ -53,6 +58,9 @@ class LevelPipeline:
level_logger.info(f"❌ No valid level found [1-10].") level_logger.info(f"❌ No valid level found [1-10].")
with open(level_logger.log_dir / "level_search_results.yaml", 'w') as f: with open(level_logger.log_dir / "level_search_results.yaml", 'w') as f:
yaml.dump(level_results, f, allow_unicode=True, sort_keys=False) yaml.dump(level_results, f, allow_unicode=True, sort_keys=False)
level_logger.logger.info(f"📂 Level search results saved to: {level_logger.log_dir / 'level_search_results.yaml'}")
if self.compress_logs:
compress_directory(level_logger.log_dir / "subtasks", delete_original=True, logger=level_logger)
return level, level_results return level, level_results
def test_level(self, level: int): def test_level(self, level: int):
@@ -63,7 +71,7 @@ class LevelPipeline:
success_mean = float(aggregated_results['summary']['success']['mean'].split(' ')[0]) success_mean = float(aggregated_results['summary']['success']['mean'].split(' ')[0])
all_success = success_mean >= 0.8 all_success = success_mean >= 0.8
if all_success: if all_success:
level_logger.info(f"✅ Level {level} passed all tests.") level_logger.info(f"✅ Level {level} passed all tests, success mean: {success_mean}.")
else: else:
level_logger.info(f"❌ Level {level} failed some tests.") level_logger.info(f"❌ Level {level} failed some tests, success mean: {success_mean}.")
return all_success, aggregated_results return all_success, aggregated_results

View File

@@ -24,6 +24,7 @@ from robogauge.utils.task_register import task_register
from robogauge.utils.logger import Logger from robogauge.utils.logger import Logger
from robogauge.utils.process_utils import NoDaemonPool from robogauge.utils.process_utils import NoDaemonPool
from robogauge.utils.progress_monitor import report_progress, ProgressTypes, ProgressData from robogauge.utils.progress_monitor import report_progress, ProgressTypes, ProgressData
from robogauge.utils.file_utils import compress_directory
multi_logger = Logger() # MultiPipeline logger multi_logger = Logger() # MultiPipeline logger
@@ -38,7 +39,8 @@ def run_single_process(args, data):
logger.create( logger.create(
experiment_name=local_args.experiment_name, experiment_name=local_args.experiment_name,
run_name=run_name, run_name=run_name,
console_output=False console_output=False,
parent_log_dir=args.parent_log_dir
) )
pipeline = task_register.make_pipeline(args=local_args, create_logger=False) pipeline = task_register.make_pipeline(args=local_args, create_logger=False)
results, warning, error = pipeline.run() results, warning, error = pipeline.run()
@@ -73,7 +75,10 @@ class MultiPipeline:
self.progress_data = progress_data self.progress_data = progress_data
self.num_processes = args.num_processes self.num_processes = args.num_processes
self.static_info = {} self.static_info = {}
multi_logger.create(args.experiment_name+'_multi', args.run_name+'_multi', console_output=console_output) parent_log_dir = getattr(args, 'parent_log_dir', None)
multi_logger.create(args.experiment_name+'_multi', args.run_name+'_multi', console_output=console_output, parent_log_dir=parent_log_dir)
self.args.parent_log_dir = str(multi_logger.log_dir / "subtasks")
self.compress_logs = args.compress_logs
def add_static_info(self, key: str, value): def add_static_info(self, key: str, value):
if key not in self.static_info: if key not in self.static_info:
@@ -153,6 +158,8 @@ class MultiPipeline:
multi_logger.info("✅ Aggregated execution finished.") multi_logger.info("✅ Aggregated execution finished.")
multi_logger.info(f"📁 Aggregated results saved to: {save_path}") multi_logger.info(f"📁 Aggregated results saved to: {save_path}")
if self.compress_logs:
compress_directory(multi_logger.log_dir / "subtasks", delete_original=True, logger=multi_logger)
# multi_logger.info( # multi_logger.info(
# f"""\n{'='*20} Multi-Run Summary {'='*20}\n""" # f"""\n{'='*20} Multi-Run Summary {'='*20}\n"""
# f"""{yaml.dump(summary, allow_unicode=True)}""" # f"""{yaml.dump(summary, allow_unicode=True)}"""

View File

@@ -7,6 +7,12 @@
@Blog : https://wty-yy.github.io/ @Blog : https://wty-yy.github.io/
@Desc : Stress Pipeline for Robogauge @Desc : Stress Pipeline for Robogauge
''' '''
# MuJoCo/XLA warnings suppression
import os
os.environ['ABSL_LOG_LEVEL'] = 'error'
import absl.logging
absl.logging.set_verbosity(absl.logging.ERROR)
import yaml import yaml
import traceback import traceback
import functools import functools
@@ -22,9 +28,15 @@ from robogauge.utils.process_utils import NoDaemonPool
from robogauge.utils.progress_monitor import report_progress, ProgressTypes, start_progress_monitor_thread, ProgressData from robogauge.utils.progress_monitor import report_progress, ProgressTypes, start_progress_monitor_thread, ProgressData
from robogauge.tasks.pipeline import MultiPipeline, LevelPipeline from robogauge.tasks.pipeline import MultiPipeline, LevelPipeline
from robogauge.tasks.gauge.gauge_configs.terrain_levels_config import TerrainSearchLevelsConfig from robogauge.tasks.gauge.gauge_configs.terrain_levels_config import TerrainSearchLevelsConfig
from robogauge.utils.file_utils import compress_directory
stress_logger = Logger() # StressPipeline logger stress_logger = Logger() # StressPipeline logger
GOALS = {
'level_pipeline': ['target_pos_velocity'],
'multi_pipeline': ['max_velocity', 'diagonal_velocity']
}
def run_pipeline(args, progress_queue, data): def run_pipeline(args, progress_queue, data):
args = deepcopy(args) args = deepcopy(args)
task_id = data['task_id'] task_id = data['task_id']
@@ -44,6 +56,7 @@ def run_pipeline(args, progress_queue, data):
args.base_masses = [data['base_mass']] args.base_masses = [data['base_mass']]
args.task_name = f"{data['task_robot_model']}.{data['terrain_name']}" args.task_name = f"{data['task_robot_model']}.{data['terrain_name']}"
args.experiment_name = f"{args.experiment_name}_{data['terrain_name']}_M{data['base_mass']}_F{data['friction']}" args.experiment_name = f"{args.experiment_name}_{data['terrain_name']}_M{data['base_mass']}_F{data['friction']}"
args.goals = GOALS['level_pipeline']
level, level_results = LevelPipeline(args, console_output=False, progress_data=progress_data).run() level, level_results = LevelPipeline(args, console_output=False, progress_data=progress_data).run()
if level == 0: # no valid level found if level == 0: # no valid level found
@@ -63,6 +76,7 @@ def run_pipeline(args, progress_queue, data):
args.experiment_name = f"{args.experiment_name}_{data['terrain_name']}" args.experiment_name = f"{args.experiment_name}_{data['terrain_name']}"
args.level = level args.level = level
args.goals = GOALS['multi_pipeline']
results = { results = {
'success': True, 'success': True,
'results': MultiPipeline(args, console_output=False, progress_data=progress_data).run(), 'results': MultiPipeline(args, console_output=False, progress_data=progress_data).run(),
@@ -81,6 +95,9 @@ class StressPipeline:
args.experiment_name = self.task_robot_model + '_stress' + ('' if args.cli_experiment_name is None else '_' + args.cli_experiment_name) args.experiment_name = self.task_robot_model + '_stress' + ('' if args.cli_experiment_name is None else '_' + args.cli_experiment_name)
self.static_info = {} self.static_info = {}
stress_logger.create(args.experiment_name, args.run_name) stress_logger.create(args.experiment_name, args.run_name)
self.args.parent_log_dir = str(stress_logger.log_dir / "subtasks")
self.compress_logs = args.compress_logs
args.compress_logs = False # Disable child log compression
def add_static_info(self, key: str, value): def add_static_info(self, key: str, value):
if key not in self.static_info: if key not in self.static_info:
@@ -183,7 +200,9 @@ class StressPipeline:
for metric, means in result['results']['summary'].items(): for metric, means in result['results']['summary'].items():
for mean_name, mean_value in means.items(): for mean_name, mean_value in means.items():
value_collections[metric][mean_name].append(float(mean_value.split(' ')[0])) value = float(mean_value.split(' ')[0])
value += (terrain_level - 1) * 0.1 if terrain_level is not None else 0.0
value_collections[metric][mean_name].append(value)
for metric, means in value_collections.items(): for metric, means in value_collections.items():
summary['summary'][metric] = {} summary['summary'][metric] = {}
@@ -196,4 +215,7 @@ class StressPipeline:
yaml.dump(summary, file, allow_unicode=True, sort_keys=False) yaml.dump(summary, file, allow_unicode=True, sort_keys=False)
stress_logger.info(f"✅ Stress benchmark aggregated execution finished.") stress_logger.info(f"✅ Stress benchmark aggregated execution finished.")
stress_logger.info(f"📁 Stress benchmark results saved to: {save_path}") stress_logger.info(f"📁 Stress benchmark results saved to: {save_path}")
if self.compress_logs:
compress_directory(stress_logger.log_dir / "subtasks", delete_original=True, logger=stress_logger)
return summary return summary

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
'''
@File : file_utils.py
@Time : 2025/12/27 17:14:29
@Author : wty-yy
@Version : 1.0
@Blog : https://wty-yy.github.io/
@Desc : Common File Utilities
'''
import tarfile
import shutil
import os
from pathlib import Path
import logging
def log_msg(logger: logging.Logger, msg: str, level: str):
if logger:
getattr(logger, level)(msg)
else:
print(msg)
def compress_directory(source_dir, output_filename=None, delete_original=True, logger: logging.Logger=None):
"""
Compress a directory into a .tar.xz file using LZMA compression.
Args:
source_dir (str | Path): Path to the directory to compress.
output_filename (str | Path, optional): Output path. Defaults to source_dir + ".tar.xz".
delete_original (bool): Whether to delete the source directory after successful compression.
"""
source_dir = Path(source_dir)
if not source_dir.exists():
log_msg(logger, f"⚠️ Source directory for compression not found: {source_dir}", "warning")
return
if output_filename is None:
output_filename = source_dir.with_suffix('.tar.xz')
else:
output_filename = Path(output_filename)
log_msg(logger, f"📦 Compressing logs: {source_dir.name} -> {output_filename.name} ...", "info")
try:
with tarfile.open(output_filename, "w:xz") as tar:
tar.add(source_dir, arcname=source_dir.name)
log_msg(logger, f"✅ Compression finished: {output_filename}", "info")
if delete_original:
shutil.rmtree(source_dir)
log_msg(logger, f"🗑️ Deleted original directory: {source_dir}", "info")
except Exception as e:
log_msg(logger, f"❌ Failed to compress directory {source_dir}: {e}", "error")
# If compression failed, ensure we don't leave a half-baked file
if output_filename.exists():
os.remove(output_filename)

View File

@@ -76,6 +76,7 @@ def parse_args():
{"name": "--base-mass", "type": float, "default": 0.0, "help": "Set the base mass of the robot."}, {"name": "--base-mass", "type": float, "default": 0.0, "help": "Set the base mass of the robot."},
{"name": "--friction", "type": float, "default": 1.0, "help": "Set the ground friction coefficient."}, {"name": "--friction", "type": float, "default": 1.0, "help": "Set the ground friction coefficient."},
{"name": "--level", "type": int, "help": "Set the difficulty level of the environment, range 1-10 (flat is 0)."}, {"name": "--level", "type": int, "help": "Set the difficulty level of the environment, range 1-10 (flat is 0)."},
{"name": "--goals", "type": str, "nargs": "+", "help": "List of goal names to evaluate."},
# Multiprocessing parameters, with different seeds # Multiprocessing parameters, with different seeds
{"name": "--multi", "action": "store_true", "default": False, "help": "Enable multiprocessing."}, {"name": "--multi", "action": "store_true", "default": False, "help": "Enable multiprocessing."},
@@ -91,6 +92,8 @@ def parse_args():
{"name": "--stress-benchmark", "action": "store_true", "default": False, "help": "Use stress pipeline to benchmark model robustness."}, {"name": "--stress-benchmark", "action": "store_true", "default": False, "help": "Use stress pipeline to benchmark model robustness."},
{"name": "--stress-terrain-names", "type": str, "nargs": "+", "default": ["flat", "slope", "wave", "stairs_up", "stairs_down"], "help": "List of terrain names for stress benchmark."}, {"name": "--stress-terrain-names", "type": str, "nargs": "+", "default": ["flat", "slope", "wave", "stairs_up", "stairs_down"], "help": "List of terrain names for stress benchmark."},
{"name": "--stress-num-processes", "type": int, "default": 2, "help": "Number of parallel processes for stress benchmark."}, {"name": "--stress-num-processes", "type": int, "default": 2, "help": "Number of parallel processes for stress benchmark."},
{"name": "--compress-logs", "action": "store_true", "default": False, "help": "Compress and delete logs after run."},
] ]
for param in parameters: for param in parameters:
parser.add_argument(param['name'], **{k: v for k, v in param.items() if k != 'name'}) parser.add_argument(param['name'], **{k: v for k, v in param.items() if k != 'name'})

View File

@@ -58,7 +58,8 @@ class Logger:
experiment_name, experiment_name,
run_name, run_name,
console_output=True, color_output=True, console_output=True, color_output=True,
log_level=logging.DEBUG, save_file_mode='a' log_level=logging.DEBUG, save_file_mode='a',
parent_log_dir=None
): ):
""" """
Create customed Logger Create customed Logger
@@ -69,6 +70,7 @@ class Logger:
color_output (bool, optional): Whether use color output. Defaults to True. color_output (bool, optional): Whether use color output. Defaults to True.
log_level (int, optional): Defaults to logging.DEBUG. log_level (int, optional): Defaults to logging.DEBUG.
save_file_mode (str, optional): The mode of saving to path_log_file save_file_mode (str, optional): The mode of saving to path_log_file
parent_log_dir (Path | str, optional): If specified, log_dir will be created under this directory.
Returns: Returns:
logging.Logger: logger logging.Logger: logger
@@ -102,7 +104,11 @@ class Logger:
sh.setFormatter(console_formatter) sh.setFormatter(console_formatter)
self.logger.addHandler(sh) self.logger.addHandler(sh)
self.log_dir = Path(ROBOGAUGE_LOGS_DIR) / experiment_name / self.tag if parent_log_dir:
baes_dir = Path(parent_log_dir)
else:
baes_dir = Path(ROBOGAUGE_LOGS_DIR)
self.log_dir = baes_dir / experiment_name / self.tag
self.log_dir.mkdir(parents=True, exist_ok=True) self.log_dir.mkdir(parents=True, exist_ok=True)
path_log_file = self.log_dir / "stdout.log" path_log_file = self.log_dir / "stdout.log"
if path_log_file: if path_log_file:

View File

@@ -9,7 +9,7 @@
''' '''
from robogauge import ROBOGAUGE_ROOT_DIR from robogauge import ROBOGAUGE_ROOT_DIR
from robogauge.utils.logger import logger from robogauge.utils.logger import logger
from robogauge.utils.helpers import parse_args, set_seed from robogauge.utils.helpers import parse_args, set_seed, class_to_dict
from robogauge.tasks.gauge.gauge_configs.terrain_levels_config import TerrainSearchLevelsConfig from robogauge.tasks.gauge.gauge_configs.terrain_levels_config import TerrainSearchLevelsConfig
class TaskRegister(): class TaskRegister():
@@ -73,14 +73,23 @@ class TaskRegister():
if args.level is not None: if args.level is not None:
gauger_cfg.assets.terrain_level = args.level gauger_cfg.assets.terrain_level = args.level
levels_cfg = TerrainSearchLevelsConfig() levels_cfg = TerrainSearchLevelsConfig()
cfg = getattr(levels_cfg, gauger_cfg.assets.terrain_name) cfg = getattr(levels_cfg, gauger_cfg.assets.terrain_name, None)
assert cfg is not None, f"Level {args.level} configuration not found in TerrainLevelsConfig." assert cfg is not None, f"Level {args.level} configuration not found in TerrainLevelsConfig."
assert args.level in cfg.levels, f"Level must be in {cfg.levels}." assert args.level in cfg.levels, f"Level must be in {cfg.levels}."
gauger_cfg.goals.target_pos_velocity.target_pos = cfg.targets[cfg.levels.index(args.level)] if hasattr(cfg, 'targets'):
gauger_cfg.goals.target_pos_velocity.target_pos = cfg.targets[cfg.levels.index(args.level)]
if hasattr(cfg, 'spawns'): if hasattr(cfg, 'spawns'):
gauger_cfg.assets.terrain_spawn_pos = cfg.spawns[cfg.levels.index(args.level)] gauger_cfg.assets.terrain_spawn_pos = cfg.spawns[cfg.levels.index(args.level)]
xml = gauger_cfg.assets.terrain_xmls[0] xml = gauger_cfg.assets.terrain_xmls[0]
xml = xml.rsplit('/', 1)[0] + f"/{gauger_cfg.assets.terrain_name}_{args.level}.xml" xml = xml.rsplit('/', 1)[0] + f"/{gauger_cfg.assets.terrain_name}_{args.level}.xml"
gauger_cfg.assets.terrain_xmls[0] = xml gauger_cfg.assets.terrain_xmls[0] = xml
if args.goals is not None:
keys = class_to_dict(gauger_cfg.goals).keys()
enable_count = 0
for key in keys:
flag = key in args.goals
getattr(gauger_cfg.goals, key).enabled = flag
enable_count += int(flag)
assert enable_count > 0, f"At least one goal must be enabled from '{args.goals}', available goals are {list(keys)}."
task_register = TaskRegister() task_register = TaskRegister()

View File

@@ -10,14 +10,12 @@ setup(
packages=find_packages(), packages=find_packages(),
install_requires=[ install_requires=[
"numpy>=1.20.0", "numpy>=1.20.0",
"mujoco>=3.0.0", # 必须依赖 "mujoco>=3.0.0",
"dm_control>=1.0.14", # 之前推荐用来组合地形的库 "dm_control>=1.0.14",
"scipy", # 计算信号处理、统计指标常用 "scipy",
"matplotlib", # 用于画出评估图表 "matplotlib",
"pandas", # 用于生成评估报告表格 "tqdm",
"tqdm", # 显示评估进度条 "imageio[ffmpeg]",
# "gymnasium", # 如果你的接口兼容 gym
# "torch", # 如果你需要加载 pytorch 模型
], ],
python_requires=">=3.8", python_requires=">=3.8",