PingPongRealWorldRL / runtime /observation.py
KurisiTOMA's picture
chore: publish H2 real-world RL system
d8290eb verified
Raw History Blame Contribute Delete
2.95 kB
"""Policy observation assembly (``PolicyCfg`` of the Online-CLoSD ball task).
Order and semantics (N = number of actuated joints):
task_command 9 | ref joint_pos N | ref joint_vel N | root xy 2 | ref pelvis xy 2 |
ref pelvis 6-D 6 | projected gravity 3 | heading xy 2 | base ang vel (body) 3 |
joint_pos - default N | joint_vel N | previous training-clipped action N -> 27 + 5N
Noise is not added (deployment = clean sensors); enable ``noise=True`` for a
training-like corruption.
"""
from __future__ import annotations
import numpy as np
from .mathutil import quat_apply, quat_apply_inverse, rot6d_interleaved
_TASK_NOISE = np.array([0.005, 0.005, 0.008, 0.008, 0.05, 0.05, 0.05, 0.03, 0.03])
def build_observation(
*,
task_command: np.ndarray,
ref_joint_pos: np.ndarray,
ref_joint_vel: np.ndarray,
root_xy: np.ndarray,
ref_pelvis_xy: np.ndarray,
ref_pelvis_quat: np.ndarray,
pelvis_quat: np.ndarray,
heading_quat: np.ndarray | None,
base_ang_vel_body: np.ndarray,
joint_pos: np.ndarray,
joint_vel: np.ndarray,
default_joint_pos: np.ndarray,
prev_action: np.ndarray,
noise: bool = False,
rng: np.random.Generator | None = None,
) -> np.ndarray:
gravity = quat_apply_inverse(pelvis_quat, np.array([0.0, 0.0, -1.0]))
# Gravity and angular velocity come from the robot IMU. Heading can use
# the sign-normalised mocap quaternion to avoid unobservable IMU yaw drift.
heading_source = pelvis_quat if heading_quat is None else heading_quat
heading = quat_apply(heading_source, np.array([1.0, 0.0, 0.0]))[:2]
heading = heading / max(float(np.linalg.norm(heading)), 1e-6)
parts = [
np.asarray(task_command, dtype=np.float64),
np.asarray(ref_joint_pos, dtype=np.float64),
np.asarray(ref_joint_vel, dtype=np.float64),
np.asarray(root_xy, dtype=np.float64),
np.asarray(ref_pelvis_xy, dtype=np.float64),
rot6d_interleaved(np.asarray(ref_pelvis_quat, dtype=np.float64)),
gravity,
heading,
np.asarray(base_ang_vel_body, dtype=np.float64),
np.asarray(joint_pos, dtype=np.float64) - np.asarray(default_joint_pos, dtype=np.float64),
np.asarray(joint_vel, dtype=np.float64),
np.asarray(prev_action, dtype=np.float64),
]
if noise:
rng = rng or np.random.default_rng()
n = len(joint_pos)
parts[0] = parts[0] + rng.uniform(-_TASK_NOISE, _TASK_NOISE)
parts[3] = parts[3] + rng.uniform(-0.05, 0.05, 2)
parts[6] = parts[6] + rng.uniform(-0.03, 0.03, 3)
parts[7] = parts[7] + rng.uniform(-0.04, 0.04, 2)
parts[8] = parts[8] + rng.uniform(-0.2, 0.2, 3)
parts[9] = parts[9] + rng.uniform(-0.01, 0.01, n)
parts[10] = parts[10] + rng.uniform(-1.0, 1.0, n)
return np.concatenate(parts).astype(np.float32)
def expected_obs_dim(num_actions: int) -> int:
return 27 + 5 * num_actions