diff --git a/UPDATE.md b/UPDATE.md index 25cd7e0..6497b0e 100644 --- a/UPDATE.md +++ b/UPDATE.md @@ -1,4 +1,24 @@ # UPDATE +## 20251225 +### v0.1.15 +1. 统一terrain的大小为10x10m, 某一边的中点在(0,0,0), 对全部的带等级的terrain都加上边界墙高度10m +2. 完成stairs_up调试台阶高度范围改为(0.05-0.23m)非线性增加, 简单测试了5个种子下moe和mlp能达到的最大地形等级 + + | frction | 0.5 | 0.75 | 1.0 | 1.5 | 2.0 | + | - | - | - | - | - | - | + | moe | 1 | 7 | 9 | 8 | 10 | + | mlp | 1 | 4 | 7 | 8 | 9 | + +3. 发现有穿模问题, 加入穿模判定0.035m, BaesPipeline新增一个warning穿模报错, 发生穿模重置当前goal并重置环境, 不计入失败 +4. 发现可以通过同时增加台阶的宽度从而避免产生体积为0的接触点, 一定程度减少穿模问题 +5. 判定Level通过比例为0.8, 也就是5个种子有4个通过即可 +6. 完成stairs_down地形调试, 简单比较moe和mlp通过地形难度, 初始点和中点在y方向上有3m差距, 避免直接滚下去恰好通过 + + | frction | 0.5 | 0.75 | 1.0 | 1.5 | 2.0 | + | - | - | - | - | - | - | + | moe | 5 | 10 | 10 | 10 | 10 | + | mlp | 3 | 6 | 7 | 10 | 10 | + ## 20251224 ### v0.1.14 1. 处理LevelPipeline返回的result信息, 保存最高等级可通过地形返回的aggregated_results和level等级 diff --git a/resources/terrains/slope/slope_1.xml b/resources/terrains/slope/slope_1.xml index 7b86709..c252ef2 100644 --- a/resources/terrains/slope/slope_1.xml +++ b/resources/terrains/slope/slope_1.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_10.xml b/resources/terrains/slope/slope_10.xml index dff0a69..257c4f9 100644 --- a/resources/terrains/slope/slope_10.xml +++ b/resources/terrains/slope/slope_10.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_2.xml b/resources/terrains/slope/slope_2.xml index d29e1b7..c829790 100644 --- a/resources/terrains/slope/slope_2.xml +++ b/resources/terrains/slope/slope_2.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_3.xml b/resources/terrains/slope/slope_3.xml index fa64ae6..e08ffee 100644 --- a/resources/terrains/slope/slope_3.xml +++ b/resources/terrains/slope/slope_3.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_4.xml b/resources/terrains/slope/slope_4.xml index 3803000..319d7af 100644 --- a/resources/terrains/slope/slope_4.xml +++ b/resources/terrains/slope/slope_4.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_5.xml b/resources/terrains/slope/slope_5.xml index 6668868..b718bb0 100644 --- a/resources/terrains/slope/slope_5.xml +++ b/resources/terrains/slope/slope_5.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_6.xml b/resources/terrains/slope/slope_6.xml index 93dc2b3..dc98973 100644 --- a/resources/terrains/slope/slope_6.xml +++ b/resources/terrains/slope/slope_6.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_7.xml b/resources/terrains/slope/slope_7.xml index 330de1c..0dd2c95 100644 --- a/resources/terrains/slope/slope_7.xml +++ b/resources/terrains/slope/slope_7.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_8.xml b/resources/terrains/slope/slope_8.xml index 6ec33c1..b15c594 100644 --- a/resources/terrains/slope/slope_8.xml +++ b/resources/terrains/slope/slope_8.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/slope/slope_9.xml b/resources/terrains/slope/slope_9.xml index 57c7e8f..d5552cf 100644 --- a/resources/terrains/slope/slope_9.xml +++ b/resources/terrains/slope/slope_9.xml @@ -1,5 +1,5 @@ - + @@ -14,6 +14,6 @@ - + diff --git a/resources/terrains/stairs_down/stairs_down_1.xml b/resources/terrains/stairs_down/stairs_down_1.xml new file mode 100644 index 0000000..88830b5 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_1.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_10.xml b/resources/terrains/stairs_down/stairs_down_10.xml new file mode 100644 index 0000000..0c801c2 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_10.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_2.xml b/resources/terrains/stairs_down/stairs_down_2.xml new file mode 100644 index 0000000..1640ee6 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_2.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_3.xml b/resources/terrains/stairs_down/stairs_down_3.xml new file mode 100644 index 0000000..1f6e4a7 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_3.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_4.xml b/resources/terrains/stairs_down/stairs_down_4.xml new file mode 100644 index 0000000..7dbac2f --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_4.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_5.xml b/resources/terrains/stairs_down/stairs_down_5.xml new file mode 100644 index 0000000..636fbd4 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_5.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_6.xml b/resources/terrains/stairs_down/stairs_down_6.xml new file mode 100644 index 0000000..2c43342 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_6.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_7.xml b/resources/terrains/stairs_down/stairs_down_7.xml new file mode 100644 index 0000000..8303c39 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_7.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_8.xml b/resources/terrains/stairs_down/stairs_down_8.xml new file mode 100644 index 0000000..0b29551 --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_8.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_down/stairs_down_9.xml b/resources/terrains/stairs_down/stairs_down_9.xml new file mode 100644 index 0000000..425d97c --- /dev/null +++ b/resources/terrains/stairs_down/stairs_down_9.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_1.xml b/resources/terrains/stairs_up/stairs_up_1.xml new file mode 100644 index 0000000..71c1230 --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_1.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_10.xml b/resources/terrains/stairs_up/stairs_up_10.xml new file mode 100644 index 0000000..b7d9f50 --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_10.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_2.xml b/resources/terrains/stairs_up/stairs_up_2.xml new file mode 100644 index 0000000..badcb3a --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_2.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_3.xml b/resources/terrains/stairs_up/stairs_up_3.xml new file mode 100644 index 0000000..5b69914 --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_3.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_4.xml b/resources/terrains/stairs_up/stairs_up_4.xml new file mode 100644 index 0000000..b39461b --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_4.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_5.xml b/resources/terrains/stairs_up/stairs_up_5.xml new file mode 100644 index 0000000..fc7dc62 --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_5.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_6.xml b/resources/terrains/stairs_up/stairs_up_6.xml new file mode 100644 index 0000000..0fe8990 --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_6.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_7.xml b/resources/terrains/stairs_up/stairs_up_7.xml new file mode 100644 index 0000000..7451e9f --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_7.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_8.xml b/resources/terrains/stairs_up/stairs_up_8.xml new file mode 100644 index 0000000..593035f --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_8.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/stairs_up/stairs_up_9.xml b/resources/terrains/stairs_up/stairs_up_9.xml new file mode 100644 index 0000000..db33eeb --- /dev/null +++ b/resources/terrains/stairs_up/stairs_up_9.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/terrains/wall/10x10_wall.xml b/resources/terrains/wall/10x10_wall.xml new file mode 100644 index 0000000..130f61b --- /dev/null +++ b/resources/terrains/wall/10x10_wall.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/robogauge/tasks/__init__.py b/robogauge/tasks/__init__.py index ad85e3f..2dbfb3c 100644 --- a/robogauge/tasks/__init__.py +++ b/robogauge/tasks/__init__.py @@ -13,3 +13,7 @@ task_register.register('go2_slope', BasePipeline, Go2SlopeMujocoConfig, Go2Slope task_register.register('go2_moe_slope', BasePipeline, Go2SlopeMujocoConfig, Go2SlopeGaugeConfig, Go2MoEConfig) task_register.register('go2_wave', BasePipeline, MujocoConfig, Go2WaveGaugeConfig, Go2Config) task_register.register('go2_moe_wave', BasePipeline, MujocoConfig, Go2WaveGaugeConfig, Go2MoEConfig) +task_register.register('go2_stairs_up', BasePipeline, MujocoConfig, Go2StairsUpGaugeConfig, Go2Config) +task_register.register('go2_moe_stairs_up', BasePipeline, MujocoConfig, Go2StairsUpGaugeConfig, Go2MoEConfig) +task_register.register('go2_stairs_down', BasePipeline, MujocoConfig, Go2StairsDownGaugeConfig, Go2Config) +task_register.register('go2_moe_stairs_down', BasePipeline, MujocoConfig, Go2StairsDownGaugeConfig, Go2MoEConfig) \ No newline at end of file diff --git a/robogauge/tasks/custom/go2/__init__.py b/robogauge/tasks/custom/go2/__init__.py index 1e11955..86b064b 100644 --- a/robogauge/tasks/custom/go2/__init__.py +++ b/robogauge/tasks/custom/go2/__init__.py @@ -9,3 +9,5 @@ from .go2_flat_task import Go2FlatGaugeConfig from .go2_slope_task import Go2SlopeGaugeConfig, Go2SlopeMujocoConfig from .go2_wave_task import Go2WaveGaugeConfig +from .go2_stairs_up_task import Go2StairsUpGaugeConfig +from .go2_stairs_down_task import Go2StairsDownGaugeConfig diff --git a/robogauge/tasks/custom/go2/go2_stairs_down_task.py b/robogauge/tasks/custom/go2/go2_stairs_down_task.py new file mode 100644 index 0000000..6896d67 --- /dev/null +++ b/robogauge/tasks/custom/go2/go2_stairs_down_task.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +''' +@File : go2_stairs_down_task.py +@Time : 2025/12/25 16:36:18 +@Author : wty-yy +@Version : 1.0 +@Blog : https://wty-yy.github.io/ +@Desc : Go2 Stairs Down Task Configuration +''' +from robogauge.tasks.robots import Go2Config, Go2MoEConfig +from robogauge.tasks.gauge import StairsDownGaugeConfig +from robogauge.tasks.simulator.mujoco_config import MujocoConfig + +class Go2StairsDownGaugeConfig(StairsDownGaugeConfig): + class metrics(StairsDownGaugeConfig.metrics): + class dof_limits(StairsDownGaugeConfig.metrics.dof_limits): + enabled = True + soft_dof_limit_ratio = 0.7 + dof_names = ['hip', 'thigh'] # List of DOF names to monitor, None for all diff --git a/robogauge/tasks/custom/go2/go2_stairs_up_task.py b/robogauge/tasks/custom/go2/go2_stairs_up_task.py new file mode 100644 index 0000000..c108f91 --- /dev/null +++ b/robogauge/tasks/custom/go2/go2_stairs_up_task.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +''' +@File : go2_wave_task.py +@Time : 2025/12/22 11:02:23 +@Author : wty-yy +@Version : 1.0 +@Blog : https://wty-yy.github.io/ +@Desc : Go2 Wave Task Configuration +''' +from robogauge.tasks.robots import Go2Config, Go2MoEConfig +from robogauge.tasks.gauge import StairsUpGaugeConfig +from robogauge.tasks.simulator.mujoco_config import MujocoConfig + +class Go2StairsUpGaugeConfig(StairsUpGaugeConfig): + class metrics(StairsUpGaugeConfig.metrics): + class dof_limits(StairsUpGaugeConfig.metrics.dof_limits): + enabled = True + soft_dof_limit_ratio = 0.7 + dof_names = ['hip', 'thigh'] # List of DOF names to monitor, None for all diff --git a/robogauge/tasks/custom/go2/go2_wave_task.py b/robogauge/tasks/custom/go2/go2_wave_task.py index c98237e..f80976a 100644 --- a/robogauge/tasks/custom/go2/go2_wave_task.py +++ b/robogauge/tasks/custom/go2/go2_wave_task.py @@ -1,12 +1,4 @@ -# -*- coding: utf-8 -*- -''' -@File : go2_wave_task.py -@Time : 2025/12/22 11:02:23 -@Author : wty-yy -@Version : 1.0 -@Blog : https://wty-yy.github.io/ -@Desc : Go2 Wave Task Configuration -''' + from robogauge.tasks.robots import Go2Config, Go2MoEConfig from robogauge.tasks.gauge import WaveGaugeConfig from robogauge.tasks.simulator.mujoco_config import MujocoConfig diff --git a/robogauge/tasks/gauge/__init__.py b/robogauge/tasks/gauge/__init__.py index 11f4776..e0fcddb 100644 --- a/robogauge/tasks/gauge/__init__.py +++ b/robogauge/tasks/gauge/__init__.py @@ -3,3 +3,5 @@ from .base_gauge_config import BaseGaugeConfig from .gauge_configs.flat_gauge_config import FlatGaugeConfig from .gauge_configs.slope_gauge_config import SlopeGaugeConfig from .gauge_configs.wave_gauge_config import WaveGaugeConfig +from .gauge_configs.stairs_up_gauge_config import StairsUpGaugeConfig +from .gauge_configs.stairs_down_gauge_config import StairsDownGaugeConfig diff --git a/robogauge/tasks/gauge/base_gauge.py b/robogauge/tasks/gauge/base_gauge.py index 69ae50e..dbe9b2c 100644 --- a/robogauge/tasks/gauge/base_gauge.py +++ b/robogauge/tasks/gauge/base_gauge.py @@ -112,6 +112,13 @@ class BaseGauge: logger.info(f"New Goal [{self.goal_idx+1}/{len(self.goals)}] [{goal_obj.count+1}/{goal_obj.total}]: {self.goal_str}") return goal + def reset_current_goal(self): + """ Reset the current goal """ + if self.goal_idx >= len(self.goals): + return + goal_obj = self.goals[self.goal_idx] + goal_obj.reset_goal() + def switch_to_next_goal(self): """ Switch to the next goal and log the metrics of the current goal. """ goal_obj = self.goals[self.goal_idx] diff --git a/robogauge/tasks/gauge/base_gauge_config.py b/robogauge/tasks/gauge/base_gauge_config.py index f09676c..3f2e14b 100644 --- a/robogauge/tasks/gauge/base_gauge_config.py +++ b/robogauge/tasks/gauge/base_gauge_config.py @@ -16,7 +16,7 @@ class BaseGaugeConfig(Config): class assets: terrain_name = "flat" terrain_level = 0 - terrain_xml = '{ROBOGAUGE_ROOT_DIR}/resources/terrains/flat.xml' + terrain_xmls = ['{ROBOGAUGE_ROOT_DIR}/resources/terrains/flat.xml'] terrain_spawn_pos = [0, 0, 0] # x y z [m], robot freejoint spawn position on the terrain class goals: diff --git a/robogauge/tasks/gauge/gauge_configs/flat_gauge_config.py b/robogauge/tasks/gauge/gauge_configs/flat_gauge_config.py index 48ee998..6a22dc1 100644 --- a/robogauge/tasks/gauge/gauge_configs/flat_gauge_config.py +++ b/robogauge/tasks/gauge/gauge_configs/flat_gauge_config.py @@ -15,5 +15,5 @@ class FlatGaugeConfig(BaseGaugeConfig): class assets(BaseGaugeConfig.assets): terrain_name = "flat" terrain_level = 0 - terrain_xml = '{ROBOGAUGE_ROOT_DIR}/resources/terrains/flat.xml' + terrain_xmls = ['{ROBOGAUGE_ROOT_DIR}/resources/terrains/flat.xml'] terrain_spawn_pos = [0, 0, 0] # x y z [m], robot freejoint spawn position on the terrain diff --git a/robogauge/tasks/gauge/gauge_configs/slope_gauge_config.py b/robogauge/tasks/gauge/gauge_configs/slope_gauge_config.py index 74b93a9..280e214 100644 --- a/robogauge/tasks/gauge/gauge_configs/slope_gauge_config.py +++ b/robogauge/tasks/gauge/gauge_configs/slope_gauge_config.py @@ -15,13 +15,16 @@ class SlopeGaugeConfig(BaseGaugeConfig): class assets(BaseGaugeConfig.assets): terrain_name = "slope" terrain_level = 10 # 1-10 - terrain_xml = '{ROBOGAUGE_ROOT_DIR}/resources/terrains/slope/slope_10.xml' - terrain_spawn_pos = [0, 0, 0] # x y z [m], robot freejoint spawn position on the terrain + terrain_xmls = [ + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/slope/slope_10.xml', + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/wall/10x10_wall.xml', + ] + terrain_spawn_pos = [0.8, 0, 1] # 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 = [5, 0, 2.28] # x y z [m], target position in the environment, used for target position goal + target_pos = [4, 0, 2.28] # x y z [m], target position in the environment, used for target position goal lin_vel_x = 1.0 # +/- m/s lin_vel_y = 1.0 # +/- m/s ang_vel_yaw = 1.5 # +/- rad/s diff --git a/robogauge/tasks/gauge/gauge_configs/stairs_down_gauge_config.py b/robogauge/tasks/gauge/gauge_configs/stairs_down_gauge_config.py new file mode 100644 index 0000000..0afbab2 --- /dev/null +++ b/robogauge/tasks/gauge/gauge_configs/stairs_down_gauge_config.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +''' +@File : stairs_down_gauge_config.py +@Time : 2025/12/25 14:12:59 +@Author : wty-yy +@Version : 1.0 +@Blog : https://wty-yy.github.io/ +@Desc : Stairs Down Gauge Configuration +''' +from robogauge.tasks.gauge.base_gauge_config import BaseGaugeConfig + +class StairsDownGaugeConfig(BaseGaugeConfig): + gauge_class = 'BaseGauge' + + class assets(BaseGaugeConfig.assets): + terrain_name = "stairs_down" + terrain_level = 10 # 1-10 + terrain_xmls = [ + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/stairs_down/stairs_down_10.xml', + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/wall/10x10_wall.xml', + ] + # NOTE: Adjusted y=3.0 to avoid rolling down just pass the target + terrain_spawn_pos = [1.1, 3.0, 7] # 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 = [8.3, 0.0, 1.90] # 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.3 diff --git a/robogauge/tasks/gauge/gauge_configs/stairs_up_gauge_config.py b/robogauge/tasks/gauge/gauge_configs/stairs_up_gauge_config.py new file mode 100644 index 0000000..4bb07a6 --- /dev/null +++ b/robogauge/tasks/gauge/gauge_configs/stairs_up_gauge_config.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +''' +@File : stairs_up_gauge_config.py +@Time : 2025/12/25 11:26:47 +@Author : wty-yy +@Version : 1.0 +@Blog : https://wty-yy.github.io/ +@Desc : Stairs Up Gauge Configuration +''' +from robogauge.tasks.gauge.base_gauge_config import BaseGaugeConfig + +class StairsUpGaugeConfig(BaseGaugeConfig): + gauge_class = 'BaseGauge' + + class assets(BaseGaugeConfig.assets): + terrain_name = "stairs_up" + terrain_level = 10 # 1-10 + terrain_xmls = [ + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/stairs_up/stairs_up_10.xml', + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/wall/10x10_wall.xml', + ] + terrain_spawn_pos = [1.0, 0.0, 1.5] # 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 = [4.5, 0.0, 3.8] # 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 diff --git a/robogauge/tasks/gauge/gauge_configs/terrain_levels_config.py b/robogauge/tasks/gauge/gauge_configs/terrain_levels_config.py index 49f6676..1c5b0c0 100644 --- a/robogauge/tasks/gauge/gauge_configs/terrain_levels_config.py +++ b/robogauge/tasks/gauge/gauge_configs/terrain_levels_config.py @@ -1,3 +1,12 @@ +# -*- coding: utf-8 -*- +''' +@File : terrain_levels_config.py +@Time : 2025/12/25 11:27:05 +@Author : wty-yy +@Version : 1.0 +@Blog : https://wty-yy.github.io/ +@Desc : Terrain Levels Configuration +''' from robogauge.utils.config import Config class TerrainLevelsConfig(Config): @@ -8,30 +17,84 @@ class TerrainLevelsConfig(Config): class slope: levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] targets = [ - [5, 0, 0.588], - [5, 0, 0.776], - [5, 0, 0.964], - [5, 0, 1.152], - [5, 0, 1.340], - [5, 0, 1.528], - [5, 0, 1.716], - [5, 0, 1.904], - [5, 0, 2.092], - [5, 0, 2.280], + [4, 0, 0.588 + 0.1], + [4, 0, 0.776 + 0.1], + [4, 0, 0.964 + 0.1], + [4, 0, 1.152 + 0.1], + [4, 0, 1.340 + 0.1], + [4, 0, 1.528 + 0.1], + [4, 0, 1.716 + 0.1], + [4, 0, 1.904 + 0.1], + [4, 0, 2.092 + 0.1], + [4, 0, 2.280 + 0.1], + ] + spawns = [ + [0.8, 0, 0.55 - 0.037 * 9], + [0.8, 0, 0.55 - 0.037 * 8], + [0.8, 0, 0.55 - 0.037 * 7], + [0.8, 0, 0.55 - 0.037 * 6], + [0.8, 0, 0.55 - 0.037 * 5], + [0.8, 0, 0.55 - 0.037 * 4], + [0.8, 0, 0.55 - 0.037 * 3], + [0.8, 0, 0.55 - 0.037 * 2], + [0.8, 0, 0.55 - 0.037], + [0.8, 0, 0.55], ] class wave: levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] targets = [ - [6.5, 0.0, 0.08], - [6.5, 0.0, 0.16], - [6.5, 0.0, 0.24], - [6.5, 0.0, 0.32], - [6.5, 0.0, 0.40], - [6.5, 0.0, 0.48], - [6.5, 0.0, 0.56], - [6.5, 0.0, 0.64], - [6.5, 0.0, 0.72], - [6.5, 0.0, 0.80], + [6.5, -2.65, 0.08], + [6.5, -2.65, 0.16], + [6.5, -2.65, 0.24], + [6.5, -2.65, 0.32], + [6.5, -2.65, 0.40], + [6.5, -2.65, 0.48], + [6.5, -2.65, 0.56], + [6.5, -2.65, 0.64], + [6.5, -2.65, 0.72], + [6.5, -2.65, 0.80], ] + class stairs_up: + levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + targets = [ + [4.5, 0.0, 1.35], + [4.5, 0.0, 1.80], + [4.5, 0.0, 2.25], + [4.5, 0.0, 2.70], + [4.5, 0.0, 2.85], + [4.5, 0.0, 3.00], + [4.5, 0.0, 3.15], + [4.5, 0.0, 3.30], + [4.5, 0.0, 3.45], + [4.5, 0.0, 3.60], + ] + + class stairs_down: + levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + targets = [ + [8.3, 0.0, 1.90 - 0.07 * 15], + [8.3, 0.0, 1.90 - 0.07 * 12], + [8.3, 0.0, 1.90 - 0.07 * 9], + [8.3, 0.0, 1.90 - 0.07 * 6], + [8.3, 0.0, 1.90 - 0.07 * 5], + [8.3, 0.0, 1.90 - 0.07 * 4], + [8.3, 0.0, 1.90 - 0.07 * 3], + [8.3, 0.0, 1.90 - 0.07 * 2], + [8.3, 0.0, 1.90 - 0.07], + [8.3, 0.0, 1.90], + ] + + spawns = [ + [1.1, 3.0, 7 - 0.3 * 15], + [1.1, 3.0, 7 - 0.3 * 12], + [1.1, 3.0, 7 - 0.3 * 9], + [1.1, 3.0, 7 - 0.3 * 6], + [1.1, 3.0, 7 - 0.3 * 5], + [1.1, 3.0, 7 - 0.3 * 4], + [1.1, 3.0, 7 - 0.3 * 3], + [1.1, 3.0, 7 - 0.3 * 2], + [1.1, 3.0, 7 - 0.3], + [1.1, 3.0, 7], + ] diff --git a/robogauge/tasks/gauge/gauge_configs/wave_gauge_config.py b/robogauge/tasks/gauge/gauge_configs/wave_gauge_config.py index 3f84555..50e04fc 100644 --- a/robogauge/tasks/gauge/gauge_configs/wave_gauge_config.py +++ b/robogauge/tasks/gauge/gauge_configs/wave_gauge_config.py @@ -15,13 +15,16 @@ class WaveGaugeConfig(BaseGaugeConfig): class assets(BaseGaugeConfig.assets): terrain_name = "wave" terrain_level = 10 # 1-10 - terrain_xml = '{ROBOGAUGE_ROOT_DIR}/resources/terrains/wave/wave_10.xml' + terrain_xmls = [ + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/wave/wave_10.xml', + '{ROBOGAUGE_ROOT_DIR}/resources/terrains/wall/10x10_wall.xml', + ] terrain_spawn_pos = [1.5, 1.25, 1.0] # x y z [m], robot freejoint spawn position on the terrain class goals: class target_pos_velocity: # goal to reach a target position by velocity command enabled = True - target_pos = [6.5, 0.0, 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 lin_vel_x = 1.0 # +/- m/s lin_vel_y = 1.0 # +/- m/s ang_vel_yaw = 1.5 # +/- rad/s diff --git a/robogauge/tasks/gauge/goals/base_goal.py b/robogauge/tasks/gauge/goals/base_goal.py index 4c62a05..25040c5 100644 --- a/robogauge/tasks/gauge/goals/base_goal.py +++ b/robogauge/tasks/gauge/goals/base_goal.py @@ -25,6 +25,9 @@ class BaseGoal: def pre_get_goal(self) -> bool: raise NotImplementedError + + def reset_goal(self): + raise NotImplementedError def is_reset(self, sim_data: SimData) -> bool: raise NotImplementedError diff --git a/robogauge/tasks/gauge/goals/velocity_goals.py b/robogauge/tasks/gauge/goals/velocity_goals.py index 248ba89..e02a210 100644 --- a/robogauge/tasks/gauge/goals/velocity_goals.py +++ b/robogauge/tasks/gauge/goals/velocity_goals.py @@ -33,10 +33,15 @@ class BaseVelocityGoal(BaseGoal): super().__init__() self.control_dt = control_dt self.cmd_duration = cmd_duration + self.goals = [] + self.goal_runtime = 0.0 + self.first_goal_after_reset = True + self.last_reset_time = -1 + + def reset_goal(self): self.goal_runtime = 0.0 self.first_goal_after_reset = True self.last_reset_time = -1 - self.goals = [] def is_reset(self, sim_data: SimData) -> bool: if self.last_reset_time == -1: @@ -190,6 +195,11 @@ class TargetPosVelocityGoal(BaseVelocityGoal): self.total = 1 # only one task self.done = False self.success = False + + def reset_goal(self): + super().reset_goal() + self.done = False + self.success = False def get_goal(self, sim_data: SimData) -> Optional[GoalData]: self.update_runtime_count(sim_data) diff --git a/robogauge/tasks/pipeline/base_pipeline.py b/robogauge/tasks/pipeline/base_pipeline.py index bb9029b..989cf69 100644 --- a/robogauge/tasks/pipeline/base_pipeline.py +++ b/robogauge/tasks/pipeline/base_pipeline.py @@ -53,7 +53,7 @@ class BasePipeline: def load(self): self.sim.load( - self.gauge_cfg.assets.terrain_xml, + self.gauge_cfg.assets.terrain_xmls, self.robot_cfg.assets.robot_xml, self.gauge_cfg.assets.terrain_spawn_pos, self.robot_cfg.control.default_dof_pos @@ -68,7 +68,7 @@ class BasePipeline: "Control dt must be multiple of simulation dt." logger.info(f"Sim FPS: {1.0 / self.sim_cfg.physics.simulation_dt:.2f}, Control FPS: {1.0 / self.robot_cfg.control.control_dt:.2f}, Frame Skip: {frame_skip:d}") logger.info("Running pipeline...") - error = None + warning, error = None, None while not self.gauge.is_done(): try: if self.first_reset: # wait for robot to be still @@ -77,7 +77,10 @@ class BasePipeline: velocity_goal=VelocityGoal(), # zero velocity position_goal=PositionGoal(), # current position ) - if np.linalg.norm(sim_data.proprio.base.lin_vel) < 0.05 and sim_data.sim_time - self.last_reset_time > 0.1: + if ( + (np.linalg.norm(sim_data.proprio.base.lin_vel) < 0.05 and sim_data.sim_time - self.last_reset_time > 0.1) or # robot is still + (sim_data.sim_time - self.last_reset_time) > 3.0 # wait max 3s + ): self.first_reset = False else: goal_data = self.gauge.get_goal(sim_data) @@ -106,18 +109,24 @@ class BasePipeline: if self.gauge.is_reset(sim_data): sim_data = self.reset_sim_and_robot(sim_data) except Exception as e: - error = e - logger.error(f"❌ Goal '{self.gauge.goal_str}' failed with error: {e},\n{traceback.format_exc()}") - self.gauge.switch_to_next_goal() # save current goal metrics + if str(e).startswith("[Penetration Error]"): + warning = e + logger.warning(f"⚠️ Penetration detected! Reset current goal and continue..., error: {e}") + self.gauge.reset_current_goal() + logger.info("⏩ Pipeline recovered from penetration and continued current goal 🎯.") + else: + error = e + logger.error(f"❌ Goal '{self.gauge.goal_str}' failed with error: {e},\n{traceback.format_exc()}") + self.gauge.switch_to_next_goal() # skip to next goal + logger.info("⏩ Pipeline recovered from error and continued next goal 🎯.") sim_data = self.reset_sim_and_robot(sim_data) - logger.info("⏩ Pipeline recovered from error and continued next goal 🎯.") self.sim.close_viewer() self.sim.close_video_writer() logger.info("✅ Pipeline execution finished.") logger.info(f"📁 Logging saved at: {logger.log_dir}") - return self.gauge.results, error + return self.gauge.results, warning, error def reset_sim_and_robot(self, sim_data: SimData): self.sim.reset() diff --git a/robogauge/tasks/pipeline/level_pipeline.py b/robogauge/tasks/pipeline/level_pipeline.py index cb56126..038c944 100644 --- a/robogauge/tasks/pipeline/level_pipeline.py +++ b/robogauge/tasks/pipeline/level_pipeline.py @@ -46,17 +46,16 @@ class LevelPipeline: with open(logger.log_dir / "level_search_results.yaml", 'w') as f: yaml.dump(level_results, f, allow_unicode=True, sort_keys=False) return level, level_results - - + def test_level(self, level: int) -> bool: logger.info(f"🔍 Testing level {level}...") self.args.level = level multi_pipeline = MultiPipeline(self.args) aggregated_results = multi_pipeline.run() success_mean = float(aggregated_results['success']['mean'].split(' ')[0]) - all_success = success_mean == 1.0 + all_success = success_mean >= 0.8 if all_success: logger.info(f"✅ Level {level} passed all tests.") else: logger.info(f"❌ Level {level} failed some tests.") - return all_success, aggregated_results \ No newline at end of file + return all_success, aggregated_results diff --git a/robogauge/tasks/pipeline/multi_pipeline.py b/robogauge/tasks/pipeline/multi_pipeline.py index 7c88437..b81ac3e 100644 --- a/robogauge/tasks/pipeline/multi_pipeline.py +++ b/robogauge/tasks/pipeline/multi_pipeline.py @@ -36,13 +36,15 @@ def run_single_process(args, data): console_output=False ) pipeline = task_register.make_pipeline(args=local_args, create_logger=False) - results, error = pipeline.run() + results, warning, error = pipeline.run() if error is None: ret = { 'status': 'success', 'results': results, 'model_path': pipeline.robot_cfg.control.model_path, } + if warning is not None: + logger.warning(f"⚠️ Process with seed={seed}, base_mass={base_mass}, friction={friction} completed with warning: {warning}") else: logger.error(f"❌ Process with seed={seed}, base_mass={base_mass}, friction={friction} failed with error: {error}") ret = { diff --git a/robogauge/tasks/simulator/mujoco_config.py b/robogauge/tasks/simulator/mujoco_config.py index eee905e..1df2a1b 100644 --- a/robogauge/tasks/simulator/mujoco_config.py +++ b/robogauge/tasks/simulator/mujoco_config.py @@ -49,3 +49,4 @@ class MujocoConfig(Config): class truncation: enabled = True projected_gravity_rad = 2.5 # [rad], if gravity projection angle exceeds this value, truncate episode + penetration_threshold = -0.05 # [m], if any contact penetration depth is below this threshold, truncate episode diff --git a/robogauge/tasks/simulator/mujoco_simulator.py b/robogauge/tasks/simulator/mujoco_simulator.py index 44f3443..2cb6b48 100644 --- a/robogauge/tasks/simulator/mujoco_simulator.py +++ b/robogauge/tasks/simulator/mujoco_simulator.py @@ -16,7 +16,7 @@ import time import imageio import numpy as np from pathlib import Path -from typing import Literal +from typing import Literal, List from robogauge.utils.logger import logger from robogauge.utils.helpers import parse_path @@ -30,7 +30,7 @@ from robogauge.tasks.simulator.sim_data import ( class MujocoSimulator: def __init__(self, sim_cfg: MujocoConfig): self.cfg = sim_cfg - self.terrain_xml = None + self.terrain_xmls = None self.robot_xml = None self.terrain_spawn_pos = None self.robot_spawn_height = None @@ -47,14 +47,14 @@ class MujocoSimulator: def load( self, - terrain_xml: str = None, + terrain_xmls: List[str] = None, robot_xml: str = None, terrain_spawn_pos: list = None, default_dof_pos: list = None, ): """ Load terrain and robot into the simulator, support re-loading. """ - if terrain_xml is not None: - self.terrain_xml = parse_path(terrain_xml) + if terrain_xmls is not None: + self.terrain_xmls = [parse_path(xml) for xml in terrain_xmls] if robot_xml is not None: self.robot_xml = parse_path(robot_xml) if terrain_spawn_pos is not None: @@ -62,17 +62,20 @@ class MujocoSimulator: if default_dof_pos is not None: self.default_dof_pos = default_dof_pos - terrain_xml = self.terrain_xml + terrain_xmls = self.terrain_xmls robot_xml = self.robot_xml terrain_spawn_pos = self.terrain_spawn_pos - if terrain_xml is None or robot_xml is None: + if terrain_xmls is None or robot_xml is None: raise ValueError("Terrain and robot XML paths must be provided.") if default_dof_pos is None: raise ValueError("Default DOF positions must be provided.") # Create MJCF models robot_mjcf = mjcf.from_path(robot_xml) - terrain_mjcf = mjcf.from_path(terrain_xml) + terrain_mjcf = mjcf.from_path(terrain_xmls[0]) + for path in terrain_xmls[1:]: + next_terrain = mjcf.from_path(path) + terrain_mjcf.attach(next_terrain) for j in robot_mjcf.find_all('joint'): if j.tag == 'freejoint': j.remove() @@ -265,11 +268,24 @@ class MujocoSimulator: self.check_truncation(sim_data) return sim_data + def check_penetration(self, threshold: float = -0.02): + for i in range(self.mj_data.ncon): + contact = self.mj_data.contact[i] + if contact.dist < threshold: + geom1_name = mujoco.mj_id2name(self.mj_model, mujoco.mjtObj.mjOBJ_GEOM, contact.geom1) + geom2_name = mujoco.mj_id2name(self.mj_model, mujoco.mjtObj.mjOBJ_GEOM, contact.geom2) + return True, geom1_name, geom2_name, contact.dist + return False, None, None, None + def check_truncation(self, sim_data: SimData): if self.cfg.truncation.enabled: projected_gravity = get_projected_gravity(sim_data.proprio.base.quat) if -projected_gravity[2] < np.cos(self.cfg.truncation.projected_gravity_rad): - raise RuntimeError(f"Episode truncated due to excessive projected gravity, angle: {np.arccos(-projected_gravity[2]):.3f} rad, projected: {projected_gravity}") + raise RuntimeError(f"[Roll Error] Episode truncated due to excessive projected gravity, angle: {np.arccos(-projected_gravity[2]):.3f} rad, projected: {projected_gravity}") + + # is_penetrated, geom1, geom2, dist = self.check_penetration(self.cfg.truncation.penetration_threshold) + # if is_penetrated: + # raise RuntimeError(f"[Penetration Error] Episode truncated: Penetration ({geom1} <-> {geom2}), distance: {dist}") def reset(self): """ Reset the simulator to initial state. """ diff --git a/robogauge/utils/task_register.py b/robogauge/utils/task_register.py index 1d14976..ad0ff34 100644 --- a/robogauge/utils/task_register.py +++ b/robogauge/utils/task_register.py @@ -77,8 +77,10 @@ class TaskRegister(): 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}." gauger_cfg.goals.target_pos_velocity.target_pos = cfg.targets[cfg.levels.index(args.level)] - xml = gauger_cfg.assets.terrain_xml + if hasattr(cfg, 'spawns'): + gauger_cfg.assets.terrain_spawn_pos = cfg.spawns[cfg.levels.index(args.level)] + xml = gauger_cfg.assets.terrain_xmls[0] xml = xml.rsplit('/', 1)[0] + f"/{gauger_cfg.assets.terrain_name}_{args.level}.xml" - gauger_cfg.assets.terrain_xml = xml + gauger_cfg.assets.terrain_xmls[0] = xml task_register = TaskRegister()