v0.1.13 finish all goals eval (even crash)
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
# UPDATE
|
# UPDATE
|
||||||
TODO: 即使模型崩溃也要继续测完后续的goals
|
## 20251222
|
||||||
|
### v0.1.13
|
||||||
|
1. 即使模型崩溃也要继续测完后续的goals, 但是跳过当前的sub goals
|
||||||
## 20251221
|
## 20251221
|
||||||
### v0.1.12
|
### v0.1.12
|
||||||
1. 在模型崩溃时也记录下最后的gauge信息, 修改single/multi pipeline逻辑
|
1. 在模型崩溃时也记录下最后的gauge信息, 修改single/multi pipeline逻辑
|
||||||
|
|||||||
@@ -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_moe_flat', BasePipeline, MujocoConfig, Go2FlatGaugeConfig, Go2MoEConfig)
|
||||||
task_register.register('go2_slope', BasePipeline, Go2SlopeMujocoConfig, Go2SlopeGaugeConfig, Go2Config)
|
task_register.register('go2_slope', BasePipeline, Go2SlopeMujocoConfig, Go2SlopeGaugeConfig, Go2Config)
|
||||||
task_register.register('go2_moe_slope', BasePipeline, Go2SlopeMujocoConfig, Go2SlopeGaugeConfig, Go2MoEConfig)
|
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)
|
||||||
|
|||||||
@@ -8,3 +8,4 @@
|
|||||||
'''
|
'''
|
||||||
from .go2_flat_task import Go2FlatGaugeConfig
|
from .go2_flat_task import Go2FlatGaugeConfig
|
||||||
from .go2_slope_task import Go2SlopeGaugeConfig, Go2SlopeMujocoConfig
|
from .go2_slope_task import Go2SlopeGaugeConfig, Go2SlopeMujocoConfig
|
||||||
|
from .go2_wave_task import Go2WaveGaugeConfig
|
||||||
|
|||||||
19
robogauge/tasks/custom/go2/go2_wave_task.py
Normal file
19
robogauge/tasks/custom/go2/go2_wave_task.py
Normal 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
|
||||||
@@ -2,3 +2,4 @@ from .base_gauge import BaseGauge
|
|||||||
from .base_gauge_config import BaseGaugeConfig
|
from .base_gauge_config import BaseGaugeConfig
|
||||||
from .gauge_configs.flat_gauge_config import FlatGaugeConfig
|
from .gauge_configs.flat_gauge_config import FlatGaugeConfig
|
||||||
from .gauge_configs.slope_gauge_config import SlopeGaugeConfig
|
from .gauge_configs.slope_gauge_config import SlopeGaugeConfig
|
||||||
|
from .gauge_configs.wave_gauge_config import WaveGaugeConfig
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class BaseGauge:
|
|||||||
self.info = {'goal': [], 'metric': []}
|
self.info = {'goal': [], 'metric': []}
|
||||||
self.results = {} # {'goal/sub_goal': {'metric': result}}
|
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():
|
for name, kwargs in self.goals_cfg.items():
|
||||||
if not kwargs['enabled']: continue
|
if not kwargs['enabled']: continue
|
||||||
if name == 'max_velocity':
|
if name == 'max_velocity':
|
||||||
|
|||||||
28
robogauge/tasks/gauge/gauge_configs/wave_gauge_config.py
Normal file
28
robogauge/tasks/gauge/gauge_configs/wave_gauge_config.py
Normal 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
|
||||||
@@ -61,15 +61,16 @@ class BasePipeline:
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
logger.info(f"🚀 Starting single run: {self.run_name}")
|
logger.info(f"🚀 Starting single run: {self.run_name}")
|
||||||
try:
|
self.load()
|
||||||
self.load()
|
sim_data = self.sim.step()
|
||||||
sim_data = self.sim.step()
|
frame_skip = int(self.robot_cfg.control.control_dt / self.sim_cfg.physics.simulation_dt)
|
||||||
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, \
|
||||||
assert frame_skip * self.sim_cfg.physics.simulation_dt == self.robot_cfg.control.control_dt, \
|
"Control dt must be multiple of simulation 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(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...")
|
||||||
logger.info("Running pipeline...")
|
error = None
|
||||||
while not self.gauge.is_done():
|
while not self.gauge.is_done():
|
||||||
|
try:
|
||||||
if self.first_reset: # wait for robot to be still
|
if self.first_reset: # wait for robot to be still
|
||||||
goal_data = GoalData(
|
goal_data = GoalData(
|
||||||
goal_type=self.robot_cfg.control.support_goal,
|
goal_type=self.robot_cfg.control.support_goal,
|
||||||
@@ -82,7 +83,7 @@ class BasePipeline:
|
|||||||
goal_data = self.gauge.get_goal(sim_data)
|
goal_data = self.gauge.get_goal(sim_data)
|
||||||
|
|
||||||
if goal_data is None: # Change goal
|
if goal_data is None: # Change goal
|
||||||
sim_data = self.reset_sim(sim_data)
|
sim_data = self.reset_sim_and_robot(sim_data)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if goal_data.visualization_pos is not None:
|
if goal_data.visualization_pos is not None:
|
||||||
@@ -103,25 +104,27 @@ class BasePipeline:
|
|||||||
sim_data = self.sim.step()
|
sim_data = self.sim.step()
|
||||||
self.gauge.update_metrics(sim_data, goal_data)
|
self.gauge.update_metrics(sim_data, goal_data)
|
||||||
if self.gauge.is_reset(sim_data):
|
if self.gauge.is_reset(sim_data):
|
||||||
sim_data = self.reset_sim(sim_data)
|
sim_data = self.reset_sim_and_robot(sim_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Pipeline execution failed with error: {e},\n{traceback.format_exc()}")
|
error = e
|
||||||
self.gauge.switch_to_next_goal() # save current goal metrics
|
logger.error(f"❌ Goal '{self.gauge.goal_str}' failed with error: {e},\n{traceback.format_exc()}")
|
||||||
self.gauge.save_results()
|
self.gauge.switch_to_next_goal() # save current goal metrics
|
||||||
return logger.log_dir, e
|
sim_data = self.reset_sim_and_robot(sim_data)
|
||||||
finally:
|
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 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.sim.reset()
|
||||||
self.last_reset_time = sim_data.sim_time
|
self.last_reset_time = sim_data.sim_time
|
||||||
self.first_reset = True
|
self.first_reset = True
|
||||||
sim_data = self.sim.step()
|
sim_data = self.sim.step()
|
||||||
|
self.robot.reset()
|
||||||
return sim_data
|
return sim_data
|
||||||
|
|
||||||
def add_noise(self, sim_data: SimData):
|
def add_noise(self, sim_data: SimData):
|
||||||
|
|||||||
@@ -45,3 +45,6 @@ class BaseRobot:
|
|||||||
action = np.zeros(self.num_action, dtype=np.float32)
|
action = np.zeros(self.num_action, dtype=np.float32)
|
||||||
return action, self.p_gains, self.d_gains, self.control_type
|
return action, self.p_gains, self.d_gains, self.control_type
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
""" Reset model state/history if needed """
|
||||||
|
pass
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ class Go2(BaseRobot):
|
|||||||
raise NotImplementedError(f"Goal type '{goal_data.goal_type}' not implemented in Go2 robot.")
|
raise NotImplementedError(f"Goal type '{goal_data.goal_type}' not implemented in Go2 robot.")
|
||||||
return obs
|
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):
|
def get_action(self, obs: np.ndarray):
|
||||||
obs_tensor = torch.tensor(obs, dtype=torch.float32).unsqueeze(0).to(self.device)
|
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]
|
action = self.model(obs_tensor).detach().cpu().numpy().squeeze(0)[self.model2mj_idx]
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class Go2Config(RobotConfig):
|
|||||||
|
|
||||||
class commands(RobotConfig.commands):
|
class commands(RobotConfig.commands):
|
||||||
lin_vel_x = [-2.0, 2.0] # min max [m/s]
|
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]
|
lin_vel_z = None # min max [m/s]
|
||||||
ang_vel_roll = None # min max [rad/s]
|
ang_vel_roll = None # min max [rad/s]
|
||||||
ang_vel_pitch = None # min max [rad/s]
|
ang_vel_pitch = None # min max [rad/s]
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ def parse_args():
|
|||||||
{"name": "--seed", "type": int, "default": 42, "help": "Random seed."},
|
{"name": "--seed", "type": int, "default": 42, "help": "Random seed."},
|
||||||
{"name": "--write-tensorboard", "action": "store_true", "default": False, "help": "Write tensorboard logs."},
|
{"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": "--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
|
# 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."},
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ class TaskRegister():
|
|||||||
sim_cfg.render.save_video = args.save_video
|
sim_cfg.render.save_video = args.save_video
|
||||||
if args.write_tensorboard is not None:
|
if args.write_tensorboard is not None:
|
||||||
gauger_cfg.write_tensorboard = args.write_tensorboard
|
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
|
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
|
sim_cfg.domain_rand.base_mass = args.base_mass
|
||||||
|
|
||||||
task_register = TaskRegister()
|
task_register = TaskRegister()
|
||||||
|
|||||||
Reference in New Issue
Block a user