v0.1.13 finish all goals eval (even crash)

This commit is contained in:
wty-yy
2025-12-22 20:37:43 +08:00
parent 989332b951
commit 6bbdceee2f
13 changed files with 94 additions and 29 deletions

View File

@@ -1,5 +1,7 @@
# UPDATE
TODO: 即使模型崩溃也要继续测完后续的goals
## 20251222
### v0.1.13
1. 即使模型崩溃也要继续测完后续的goals, 但是跳过当前的sub goals
## 20251221
### v0.1.12
1. 在模型崩溃时也记录下最后的gauge信息, 修改single/multi pipeline逻辑

View File

@@ -11,3 +11,5 @@ task_register.register('go2_flat', BasePipeline, MujocoConfig, Go2FlatGaugeConfi
task_register.register('go2_moe_flat', BasePipeline, MujocoConfig, Go2FlatGaugeConfig, Go2MoEConfig)
task_register.register('go2_slope', BasePipeline, Go2SlopeMujocoConfig, Go2SlopeGaugeConfig, Go2Config)
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)

View File

@@ -8,3 +8,4 @@
'''
from .go2_flat_task import Go2FlatGaugeConfig
from .go2_slope_task import Go2SlopeGaugeConfig, Go2SlopeMujocoConfig
from .go2_wave_task import Go2WaveGaugeConfig

View File

@@ -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 WaveGaugeConfig
from robogauge.tasks.simulator.mujoco_config import MujocoConfig
class Go2WaveGaugeConfig(WaveGaugeConfig):
class metrics(WaveGaugeConfig.metrics):
class dof_limits(WaveGaugeConfig.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

@@ -2,3 +2,4 @@ from .base_gauge import BaseGauge
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

View File

@@ -42,7 +42,7 @@ class BaseGauge:
self.info = {'goal': [], 'metric': []}
self.results = {} # {'goal/sub_goal': {'metric': result}}
log_str = "Initialized Gauge with Goals and Metrics:\n"
log_str = "Initialized Gauge with Goals 🎯 and Metrics 📊:\n"
for name, kwargs in self.goals_cfg.items():
if not kwargs['enabled']: continue
if name == 'max_velocity':

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
'''
@File : wave_gauge_config.py
@Time : 2025/12/22 11:02:09
@Author : wty-yy
@Version : 1.0
@Blog : https://wty-yy.github.io/
@Desc : Wave Gauge Configuration
'''
from robogauge.tasks.gauge.base_gauge_config import BaseGaugeConfig
class WaveGaugeConfig(BaseGaugeConfig):
gauge_class = 'BaseGauge'
class assets(BaseGaugeConfig.assets):
terrain_name = "wave_1" # {type}_{level}
terrain_xml = '{ROBOGAUGE_ROOT_DIR}/resources/terrains/wave/wave_1.xml'
terrain_spawn_pos = [1.5, 0, 1] # 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 = [4, 0, 1.0] # 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
max_cmd_duration = 20.0 # [s] maximum duration to reach the target position
reach_threshold = 0.1

View File

@@ -61,15 +61,16 @@ class BasePipeline:
def run(self):
logger.info(f"🚀 Starting single run: {self.run_name}")
try:
self.load()
sim_data = self.sim.step()
frame_skip = int(self.robot_cfg.control.control_dt / self.sim_cfg.physics.simulation_dt)
assert frame_skip * self.sim_cfg.physics.simulation_dt == self.robot_cfg.control.control_dt, \
"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...")
while not self.gauge.is_done():
self.load()
sim_data = self.sim.step()
frame_skip = int(self.robot_cfg.control.control_dt / self.sim_cfg.physics.simulation_dt)
assert frame_skip * self.sim_cfg.physics.simulation_dt == self.robot_cfg.control.control_dt, \
"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
while not self.gauge.is_done():
try:
if self.first_reset: # wait for robot to be still
goal_data = GoalData(
goal_type=self.robot_cfg.control.support_goal,
@@ -82,7 +83,7 @@ class BasePipeline:
goal_data = self.gauge.get_goal(sim_data)
if goal_data is None: # Change goal
sim_data = self.reset_sim(sim_data)
sim_data = self.reset_sim_and_robot(sim_data)
continue
if goal_data.visualization_pos is not None:
@@ -103,25 +104,27 @@ class BasePipeline:
sim_data = self.sim.step()
self.gauge.update_metrics(sim_data, goal_data)
if self.gauge.is_reset(sim_data):
sim_data = self.reset_sim(sim_data)
except Exception as e:
logger.error(f"❌ Pipeline execution failed with error: {e},\n{traceback.format_exc()}")
self.gauge.switch_to_next_goal() # save current goal metrics
self.gauge.save_results()
return logger.log_dir, e
finally:
self.sim.close_viewer()
self.sim.close_video_writer()
logger.info("✅ Pipeline execution finished.")
logger.info(f"📁 Logging saved at: {logger.log_dir}")
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
sim_data = self.reset_sim_and_robot(sim_data)
logger.info("⏩ Pipeline recovered from error and continued next goal 🎯.")
return logger.log_dir, None
self.sim.close_viewer()
self.sim.close_video_writer()
logger.info("✅ Pipeline execution finished.")
logger.info(f"📁 Logging saved at: {logger.log_dir}")
def reset_sim(self, sim_data: SimData):
return logger.log_dir, error
def reset_sim_and_robot(self, sim_data: SimData):
self.sim.reset()
self.last_reset_time = sim_data.sim_time
self.first_reset = True
sim_data = self.sim.step()
self.robot.reset()
return sim_data
def add_noise(self, sim_data: SimData):

View File

@@ -45,3 +45,6 @@ class BaseRobot:
action = np.zeros(self.num_action, dtype=np.float32)
return action, self.p_gains, self.d_gains, self.control_type
def reset(self):
""" Reset model state/history if needed """
pass

View File

@@ -53,6 +53,10 @@ class Go2(BaseRobot):
raise NotImplementedError(f"Goal type '{goal_data.goal_type}' not implemented in Go2 robot.")
return obs
def reset(self):
self.last_action = np.zeros(self.num_action, dtype=np.float32)
self.model.reset() # reset history
def get_action(self, obs: np.ndarray):
obs_tensor = torch.tensor(obs, dtype=torch.float32).unsqueeze(0).to(self.device)
action = self.model(obs_tensor).detach().cpu().numpy().squeeze(0)[self.model2mj_idx]

View File

@@ -48,7 +48,7 @@ class Go2Config(RobotConfig):
class commands(RobotConfig.commands):
lin_vel_x = [-2.0, 2.0] # min max [m/s]
lin_vel_y = [-1.0, 1.0] # min max [m/s]
lin_vel_y = [-2.0, 2.0] # min max [m/s]
lin_vel_z = None # min max [m/s]
ang_vel_roll = None # min max [rad/s]
ang_vel_pitch = None # min max [rad/s]

View File

@@ -73,6 +73,8 @@ def parse_args():
{"name": "--seed", "type": int, "default": 42, "help": "Random seed."},
{"name": "--write-tensorboard", "action": "store_true", "default": False, "help": "Write tensorboard logs."},
{"name": "--plot-radar", "action": "store_true", "default": False, "help": "Plot radar charts for metrics."},
{"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."},
# Multiprocessing parameters, with different seeds
{"name": "--multi", "action": "store_true", "default": False, "help": "Enable multiprocessing."},

View File

@@ -65,9 +65,9 @@ class TaskRegister():
sim_cfg.render.save_video = args.save_video
if args.write_tensorboard is not None:
gauger_cfg.write_tensorboard = args.write_tensorboard
if hasattr(args, 'friction') and args.friction is not None:
if args.friction is not None:
sim_cfg.domain_rand.friction = args.friction
if hasattr(args, 'base_mass') and args.base_mass is not None:
if args.base_mass is not None:
sim_cfg.domain_rand.base_mass = args.base_mass
task_register = TaskRegister()