AmanC007 commited on
Commit
7ff03af
·
verified ·
1 Parent(s): 8dfd6a5

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. code/snow_rl/snow_env.py +12 -15
  2. code/snow_rl/terrain.py +14 -4
code/snow_rl/snow_env.py CHANGED
@@ -76,22 +76,15 @@ def default_config() -> config_dict.ConfigDict:
76
  # its gait for snow. Scaled up ~3.5x to reach roughly a quarter of total reward: enough
77
  # to steer, short of drowning velocity tracking (whose failure mode is a policy that
78
  # stands still or creeps).
79
- # Cycle 5. Cycles 2-4 escalated every snow penalty (clearance 0.8 -> 3 -> 5, overload
80
- # 0.4 -> 1.5 -> 3.0) and the policy found the trivial solution: stop walking. Measured
81
- # body-frame forward velocity as a fraction of the 0.8 m/s command:
82
- #
83
- # v1 26% swing 0.132 survived 326 steps
84
- # v2 8% swing 0.096 survived 500
85
- # v4 1% swing 0.082 survived 491 <- standing still
86
- #
87
- # Survival time is a trap as a headline metric: not moving maximises it. Distance and
88
- # velocity tracking are the honest measures, and by those v1 was the best policy. Back
89
- # toward v1's balance, keeping the two changes that were real improvements (a reachable
90
- # stumble term, and terrain that couples depth to stiffness).
91
- cfg.reward_config.scales.snow_clearance = 1.5
92
  cfg.reward_config.scales.snow_stumble = -1.0
93
- cfg.reward_config.scales.snow_flat_efficiency = -0.3
94
- cfg.reward_config.scales.snow_overload = -0.5
95
  cfg.snow = config_dict.create(
96
  enable_forces=True,
97
  # Zeroing this blanks the estimator block in the observation while leaving snow
@@ -105,6 +98,9 @@ def default_config() -> config_dict.ConfigDict:
105
  false_safe_rate=-1.0, # <0 means "use the calibrated point-estimate rate"
106
  terrain_bank_size=TERRAIN_BANK_SIZE,
107
  terrain_seed=0,
 
 
 
108
  )
109
  return cfg
110
 
@@ -139,6 +135,7 @@ class SnowJoystick(joystick.Joystick):
139
  jax.random.split(jax.random.key(self._config.snow.terrain_seed),
140
  self._config.snow.terrain_bank_size),
141
  self._terrain_cfg,
 
142
  )
143
 
144
  # ---------------- terrain ----------------
 
76
  # its gait for snow. Scaled up ~3.5x to reach roughly a quarter of total reward: enough
77
  # to steer, short of drowning velocity tracking (whose failure mode is a policy that
78
  # stands still or creeps).
79
+ # Back to the brief's original weights. Five tuning cycles (0.8 -> 3 -> 5 on clearance,
80
+ # 0.4 -> 1.5 -> 3.0 on overload) never beat them: measured velocity tracking went
81
+ # 18% -> -5% -> -8% while survival rose, because not walking maximises survival. The
82
+ # bottleneck was never the reward weights -- it is that a flat-ground policy is thrown
83
+ # into 200 mm snow from step 0, which the curriculum now addresses instead.
84
+ cfg.reward_config.scales.snow_clearance = 0.8
 
 
 
 
 
 
 
85
  cfg.reward_config.scales.snow_stumble = -1.0
86
+ cfg.reward_config.scales.snow_flat_efficiency = -0.5
87
+ cfg.reward_config.scales.snow_overload = -0.4
88
  cfg.snow = config_dict.create(
89
  enable_forces=True,
90
  # Zeroing this blanks the estimator block in the observation while leaving snow
 
98
  false_safe_rate=-1.0, # <0 means "use the calibrated point-estimate rate"
99
  terrain_bank_size=TERRAIN_BANK_SIZE,
100
  terrain_seed=0,
101
+ # Curriculum: 1.0 is full difficulty (0-25 cm). Lower values shrink every regime's
102
+ # depth range so an early stage trains on snow the policy can actually cope with.
103
+ depth_scale=1.0,
104
  )
105
  return cfg
106
 
 
135
  jax.random.split(jax.random.key(self._config.snow.terrain_seed),
136
  self._config.snow.terrain_bank_size),
137
  self._terrain_cfg,
138
+ float(self._config.snow.depth_scale),
139
  )
140
 
141
  # ---------------- terrain ----------------
code/snow_rl/terrain.py CHANGED
@@ -112,15 +112,25 @@ def _correlated_unit_field(key: jax.Array, cfg: TerrainConfig) -> jax.Array:
112
  return jnp.clip((smooth - lo) / jnp.maximum(hi - lo, 1e-9), 0.0, 1.0)
113
 
114
 
115
- def generate(key: jax.Array, cfg: TerrainConfig = DEFAULT_CONFIG) -> SnowField:
116
- """Generate one environment's snow field. vmap over keys for a batch."""
 
 
 
 
 
 
 
 
117
  k_reg, k_depth, k_stiff, k_dens, k_wet, k_void = jax.random.split(key, 6)
118
 
119
  u = jax.random.uniform(k_reg, ())
120
  regime = (u >= REGIME_EDGES[0]).astype(jnp.int32) + (u >= REGIME_EDGES[1]).astype(jnp.int32)
121
  lows = jnp.array([r[0] for r in REGIME_DEPTH_RANGE_M])
122
  highs = jnp.array([r[1] for r in REGIME_DEPTH_RANGE_M])
123
- depth = lows[regime] + _correlated_unit_field(k_depth, cfg) * (highs[regime] - lows[regime])
 
 
124
 
125
 
126
  # Stiffness is COUPLED TO DEPTH, not independent of it. An earlier version drew the two
@@ -217,5 +227,5 @@ def sample(field: SnowField, cfg: TerrainConfig, xy_m: jax.Array) -> tuple[jax.A
217
  field.damping_ns_per_m, field.void))
218
 
219
 
220
- batched_generate = jax.vmap(generate, in_axes=(0, None))
221
  batched_sample = jax.vmap(sample, in_axes=(0, None, 0))
 
112
  return jnp.clip((smooth - lo) / jnp.maximum(hi - lo, 1e-9), 0.0, 1.0)
113
 
114
 
115
+ def generate(key: jax.Array, cfg: TerrainConfig = DEFAULT_CONFIG,
116
+ depth_scale: float = 1.0) -> SnowField:
117
+ """Generate one environment's snow field. vmap over keys for a batch.
118
+
119
+ depth_scale shrinks every regime's depth range, which is how the training curriculum
120
+ works. Measured, a policy warm-started from flat ground handles snow up to ~70 mm well
121
+ (60% velocity tracking, no falls) and collapses at ~200 mm (-13% tracking, falls in 85
122
+ steps). Starting at full difficulty it converges on the easy 70% of terrain and treats
123
+ the deep third as unsolvable noise. Ramping the ceiling gives it a reachable next step.
124
+ """
125
  k_reg, k_depth, k_stiff, k_dens, k_wet, k_void = jax.random.split(key, 6)
126
 
127
  u = jax.random.uniform(k_reg, ())
128
  regime = (u >= REGIME_EDGES[0]).astype(jnp.int32) + (u >= REGIME_EDGES[1]).astype(jnp.int32)
129
  lows = jnp.array([r[0] for r in REGIME_DEPTH_RANGE_M])
130
  highs = jnp.array([r[1] for r in REGIME_DEPTH_RANGE_M])
131
+ depth = depth_scale * (
132
+ lows[regime] + _correlated_unit_field(k_depth, cfg) * (highs[regime] - lows[regime])
133
+ )
134
 
135
 
136
  # Stiffness is COUPLED TO DEPTH, not independent of it. An earlier version drew the two
 
227
  field.damping_ns_per_m, field.void))
228
 
229
 
230
+ batched_generate = jax.vmap(generate, in_axes=(0, None, None))
231
  batched_sample = jax.vmap(sample, in_axes=(0, None, 0))