refactor height estimation logic.

This commit is contained in:
wertyuilife
2026-04-08 14:49:03 +08:00
parent c18ccc557e
commit 8a4959bb4c
2 changed files with 50 additions and 43 deletions

View File

@@ -434,7 +434,7 @@ class RewardsCfg:
params={ params={
"asset_cfg": SceneEntityCfg("robot", body_names=BASE_LINK_NAME), "asset_cfg": SceneEntityCfg("robot", body_names=BASE_LINK_NAME),
"target_height": 0.38, "target_height": 0.38,
"sensor_cfg": SceneEntityCfg("height_scanner"), "sensor_cfg": SceneEntityCfg("height_scanner_small"),
} }
) )
action_rate_l2 = RewTerm(func=mdp.action_rate_l2, weight=-0.01) action_rate_l2 = RewTerm(func=mdp.action_rate_l2, weight=-0.01)
@@ -537,7 +537,7 @@ class Go2EnvCfg(ManagerBasedRLEnvCfg):
# Update sensor periods # Update sensor periods
if self.scene.height_scanner is not None: if self.scene.height_scanner is not None:
self.scene.height_scanner.update_period = self.decimation * self.sim.dt self.scene.height_scanner.update_period = self.decimation * self.sim.dt
if getattr(self.scene, "height_scanner_small", None) is not None: if self.scene.height_scanner_small is not None:
self.scene.height_scanner_small.update_period = self.decimation * self.sim.dt self.scene.height_scanner_small.update_period = self.decimation * self.sim.dt
if self.scene.contact_forces is not None: if self.scene.contact_forces is not None:
self.scene.contact_forces.update_period = self.sim.dt self.scene.contact_forces.update_period = self.sim.dt

View File

@@ -18,6 +18,43 @@ from isaaclab.utils.math import quat_apply_inverse, yaw_quat
if TYPE_CHECKING: if TYPE_CHECKING:
from isaaclab.envs import ManagerBasedRLEnv from isaaclab.envs import ManagerBasedRLEnv
def _get_base_height(
env: ManagerBasedRLEnv,
base_height_target: float,
asset_cfg: SceneEntityCfg = SceneEntityCfg("robot"),
sensor_cfg: SceneEntityCfg | None = None,
) -> torch.Tensor:
"""Estimate base height above ground.
If a height scanner is provided, this returns:
base_height = base_z - estimated_ground_z
Otherwise, it falls back to the world-frame root height, which matches the flat-ground
interpretation used by Gym-style rewards.
Invalid ray scans preserve the previous behavior by falling back to
``estimated_ground_z = base_z - base_height_target``, which makes
``base_height == base_height_target`` for those environments.
"""
asset: RigidObject = env.scene[asset_cfg.name]
base_z = asset.data.root_pos_w[:, 2]
if sensor_cfg is None:
return base_z
sensor: RayCaster = env.scene[sensor_cfg.name]
ray_hits_z = sensor.data.ray_hits_w[..., 2]
invalid = (
torch.isnan(ray_hits_z).any(dim=1)
| torch.isinf(ray_hits_z).any(dim=1)
| (torch.max(torch.abs(ray_hits_z), dim=1).values > 1e6)
)
estimated_ground_z = torch.mean(ray_hits_z, dim=1)
fallback_ground_z = base_z - base_height_target
estimated_ground_z = torch.where(invalid, fallback_ground_z, estimated_ground_z)
return base_z - estimated_ground_z
def track_lin_vel_xy_exp( def track_lin_vel_xy_exp(
env: ManagerBasedRLEnv, std: float, command_name: str, asset_cfg: SceneEntityCfg = SceneEntityCfg("robot") env: ManagerBasedRLEnv, std: float, command_name: str, asset_cfg: SceneEntityCfg = SceneEntityCfg("robot")
@@ -31,7 +68,6 @@ def track_lin_vel_xy_exp(
dim=1, dim=1,
) )
reward = torch.exp(-lin_vel_error / std**2) reward = torch.exp(-lin_vel_error / std**2)
# reward *= torch.clamp(-env.scene["robot"].data.projected_gravity_b[:, 2], 0, 0.7) / 0.7
return reward return reward
@@ -44,7 +80,6 @@ def track_ang_vel_z_exp(
# compute the error # compute the error
ang_vel_error = torch.square(env.command_manager.get_command(command_name)[:, 2] - asset.data.root_ang_vel_b[:, 2]) ang_vel_error = torch.square(env.command_manager.get_command(command_name)[:, 2] - asset.data.root_ang_vel_b[:, 2])
reward = torch.exp(-ang_vel_error / std**2) reward = torch.exp(-ang_vel_error / std**2)
# reward *= torch.clamp(-env.scene["robot"].data.projected_gravity_b[:, 2], 0, 0.7) / 0.7
return reward return reward
@@ -472,22 +507,8 @@ def base_height_l2(
For flat terrain, target height is in the world frame. For rough terrain, For flat terrain, target height is in the world frame. For rough terrain,
sensor readings can adjust the target height to account for the terrain. sensor readings can adjust the target height to account for the terrain.
""" """
# extract the used quantities (to enable type-hinting) base_height = _get_base_height(env, target_height, asset_cfg, sensor_cfg)
asset: RigidObject = env.scene[asset_cfg.name] reward = torch.square(base_height - target_height)
if sensor_cfg is not None:
sensor: RayCaster = env.scene[sensor_cfg.name]
# Adjust the target height using the sensor data
ray_hits = sensor.data.ray_hits_w[..., 2]
if torch.isnan(ray_hits).any() or torch.isinf(ray_hits).any() or torch.max(torch.abs(ray_hits)) > 1e6:
adjusted_target_height = asset.data.root_link_pos_w[:, 2]
else:
adjusted_target_height = target_height + torch.mean(ray_hits, dim=1)
else:
# Use the provided target height directly for flat terrain
adjusted_target_height = target_height
# Compute the L2 squared penalty
reward = torch.square(asset.data.root_pos_w[:, 2] - adjusted_target_height)
# reward *= torch.clamp(-env.scene["robot"].data.projected_gravity_b[:, 2], 0, 0.7) / 0.7
return reward return reward
@@ -496,7 +517,6 @@ def lin_vel_z_l2(env: ManagerBasedRLEnv, asset_cfg: SceneEntityCfg = SceneEntity
# extract the used quantities (to enable type-hinting) # extract the used quantities (to enable type-hinting)
asset: RigidObject = env.scene[asset_cfg.name] asset: RigidObject = env.scene[asset_cfg.name]
reward = torch.square(asset.data.root_lin_vel_b[:, 2]) reward = torch.square(asset.data.root_lin_vel_b[:, 2])
# reward *= torch.clamp(-env.scene["robot"].data.projected_gravity_b[:, 2], 0, 0.7) / 0.7
return reward return reward
@@ -505,7 +525,6 @@ def ang_vel_xy_l2(env: ManagerBasedRLEnv, asset_cfg: SceneEntityCfg = SceneEntit
# extract the used quantities (to enable type-hinting) # extract the used quantities (to enable type-hinting)
asset: RigidObject = env.scene[asset_cfg.name] asset: RigidObject = env.scene[asset_cfg.name]
reward = torch.sum(torch.square(asset.data.root_ang_vel_b[:, :2]), dim=1) reward = torch.sum(torch.square(asset.data.root_ang_vel_b[:, :2]), dim=1)
# reward *= torch.clamp(-env.scene["robot"].data.projected_gravity_b[:, 2], 0, 0.7) / 0.7
return reward return reward
@@ -518,7 +537,6 @@ def undesired_contacts(env: ManagerBasedRLEnv, threshold: float, sensor_cfg: Sce
is_contact = torch.max(torch.norm(net_contact_forces[:, :, sensor_cfg.body_ids], dim=-1), dim=1)[0] > threshold is_contact = torch.max(torch.norm(net_contact_forces[:, :, sensor_cfg.body_ids], dim=-1), dim=1)[0] > threshold
# sum over contacts for each environment # sum over contacts for each environment
reward = torch.sum(is_contact, dim=1).float() reward = torch.sum(is_contact, dim=1).float()
# reward *= torch.clamp(-env.scene["robot"].data.projected_gravity_b[:, 2], 0, 0.7) / 0.7
return reward return reward
@@ -530,7 +548,6 @@ def flat_orientation_l2(env: ManagerBasedRLEnv, asset_cfg: SceneEntityCfg = Scen
# extract the used quantities (to enable type-hinting) # extract the used quantities (to enable type-hinting)
asset: RigidObject = env.scene[asset_cfg.name] asset: RigidObject = env.scene[asset_cfg.name]
reward = torch.sum(torch.square(asset.data.projected_gravity_b[:, :2]), dim=1) reward = torch.sum(torch.square(asset.data.projected_gravity_b[:, :2]), dim=1)
# reward *= torch.clamp(-env.scene["robot"].data.projected_gravity_b[:, 2], 0, 0.7) / 0.7
return reward return reward
@@ -562,6 +579,14 @@ def feet_regulation(
asset_cfg: SceneEntityCfg = SceneEntityCfg("robot"), asset_cfg: SceneEntityCfg = SceneEntityCfg("robot"),
sensor_cfg: SceneEntityCfg | None = None, sensor_cfg: SceneEntityCfg | None = None,
) -> torch.Tensor: ) -> torch.Tensor:
"""Penalize fast horizontal foot motion near the ground.
Feet that are close to the ground receive a much larger penalty for lateral motion,
while feet that are lifted during swing are penalized much less.
Physically, this discourages foot scuffing / dragging, and encourages
the robot to lift its feet before moving them quickly in the xy plane.
"""
asset: RigidObject = env.scene[asset_cfg.name] asset: RigidObject = env.scene[asset_cfg.name]
feet_ids = asset_cfg.body_ids feet_ids = asset_cfg.body_ids
@@ -569,29 +594,11 @@ def feet_regulation(
feet_pos_w = asset.data.body_pos_w[:, feet_ids, :] feet_pos_w = asset.data.body_pos_w[:, feet_ids, :]
base_pos_w = asset.data.root_pos_w.unsqueeze(1) base_pos_w = asset.data.root_pos_w.unsqueeze(1)
feet_xy_vel_w = asset.data.body_lin_vel_w[:, feet_ids, :2] feet_xy_vel_w = asset.data.body_lin_vel_w[:, feet_ids, :2]
base_z = asset.data.root_pos_w[:, 2] base_height = _get_base_height(env, base_height_target, asset_cfg, sensor_cfg)
if sensor_cfg is not None:
# estimate ground_z from rays
sensor: RayCaster = env.scene[sensor_cfg.name]
ray_hits_z = sensor.data.ray_hits_w[..., 2]
invalid = (
torch.isnan(ray_hits_z).any(dim=1)
| torch.isinf(ray_hits_z).any(dim=1)
| (torch.max(torch.abs(ray_hits_z), dim=1).values > 1e6)
)
estimated_ground_z = torch.mean(ray_hits_z, dim=1)
fallback_ground_z = base_z - base_height_target
estimated_ground_z = torch.where(invalid, fallback_ground_z, estimated_ground_z)
else:
estimated_ground_z = base_z - base_height_target
base_height = base_z - estimated_ground_z
gravity_w = torch.tensor(env.sim.cfg.gravity, device=env.device, dtype=feet_pos_w.dtype) gravity_w = torch.tensor(env.sim.cfg.gravity, device=env.device, dtype=feet_pos_w.dtype)
down_w = gravity_w / torch.norm(gravity_w) down_w = gravity_w / torch.norm(gravity_w)
# compute feet regulation reward from world inputs
delta_feet_w = feet_pos_w - base_pos_w delta_feet_w = feet_pos_w - base_pos_w
feet2base_height = torch.sum(delta_feet_w * down_w.view(1, 1, 3), dim=-1) feet2base_height = torch.sum(delta_feet_w * down_w.view(1, 1, 3), dim=-1)
feet_height = torch.clamp(base_height.unsqueeze(1) - feet2base_height, min=0.0) feet_height = torch.clamp(base_height.unsqueeze(1) - feet2base_height, min=0.0)