File size: 7,540 Bytes
d8290eb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
"""Per-robot deployment specifications (G1 / H2 / A3).

Every number here mirrors the training configuration of the corresponding
``PingPong-One-Step-<ROBOT>-FB-OnlineCLoSD-Ball-DualCritic`` task:

* base task: ``tasks/tracking/tracking_pingpong_online_closd_ball_env_cfg_FB.py``
* robot overrides: ``tasks/tracking/config/{h2,a3}/flat_env_cfg.py`` via
  ``configure_online_closd_robot``; G1 uses the base values.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path

from .paths import assets_dir, package_dir, resolve

# The 14 reference bodies tracked by the policy (tracking_pingpong_fk_ref_env_cfg_FB.REFERENCE_BODY_NAMES).
REFERENCE_BODY_NAMES: tuple[str, ...] = (
    "pelvis",
    "left_hip_roll_link",
    "left_knee_link",
    "left_ankle_roll_link",
    "right_hip_roll_link",
    "right_knee_link",
    "right_ankle_roll_link",
    "torso_link",
    "left_shoulder_roll_link",
    "left_elbow_link",
    "left_wrist_yaw_link",
    "right_shoulder_roll_link",
    "right_elbow_link",
    "right_wrist_yaw_link",
)


@dataclass(frozen=True)
class EmbodimentSpec:
    name: str
    num_actions: int
    sim2sim_module: str            # module in sim2sim_tt providing <Robot>Env + gain/armature helpers
    env_class: str
    armature_helper: str | None    # optional build_<robot>_armature_for_joint_names in that module
    mjcf: str                      # relative to assets/robot_description
    closd_bundle: str              # bundle-relative
    ready_pose: str                # bundle-relative
    policy: str                    # bundle-relative default policy
    paddle_body_name: str
    paddle_offset_xyz: tuple[float, float, float]
    paddle_offset_rpy: tuple[float, float, float]
    paddle_half_width: float
    paddle_half_height: float
    nominal_root_z: float
    hit_x_range: tuple[float, float]
    hit_y_range: tuple[float, float]
    hit_z_range: tuple[float, float]
    condition_time_range: tuple[float, float]
    condition_lateral_range: tuple[float, float]
    condition_forward_range: tuple[float, float]
    condition_height_range: tuple[float, float]
    foot_body_names: tuple[str, str]
    # The training articulation has no joint damping / Coulomb friction beyond the
    # PD actuator model. The MJCFs declare some (A3: 0.5-1.5 N路m路s/rad and
    # 0.4-1.7 N路m on every joint; H2 0.05 / 0.2; G1 friction 0.1), so they are
    # neutralised by default; ``--keep-mjcf-joint-dynamics`` keeps them.
    zero_mjcf_joint_damping: bool = True
    zero_mjcf_joint_frictionloss: bool = True
    # shared by all robots (base config)
    hit_time_range: tuple[float, float] = (0.45, 0.55)
    post_hit_resample_delay_range: tuple[float, float] = (0.15, 0.70)
    long_pause_prob: float = 0.2
    long_pause_range: tuple[float, float] = (1.5, 3.0)
    target_landing_x_range: tuple[float, float] = (2.30, 3.10)
    target_landing_y_range: tuple[float, float] = (-0.60, 0.60)
    max_delta_hit_y: float = 0.7
    stroke_split_relative_y: float = -0.125
    frozen_blend_time_s: float = 0.24
    frozen_exit_blend_time_s: float = 0.24
    closd_fps: float = 50.0
    control_dt: float = 0.02
    sim_dt: float = 0.005
    reference_body_names: tuple[str, ...] = REFERENCE_BODY_NAMES
    frozen_task_command: tuple[float, ...] = field(default=(-0.59, 0.40, 0.0, 1.0, -3.0, 0.0, 0.2, 2.87, 0.0))

    @property
    def hit_plane_x(self) -> float:
        return 0.5 * (self.hit_x_range[0] + self.hit_x_range[1])

    def mjcf_path(self) -> Path:
        return resolve(self.mjcf) if Path(self.mjcf).is_absolute() or (package_dir() / self.mjcf).exists() else assets_dir() / self.mjcf

    def bundle_path(self) -> Path:
        return resolve(self.closd_bundle)

    def ready_pose_path(self) -> Path:
        return resolve(self.ready_pose)

    def policy_path(self) -> Path:
        return resolve(self.policy)

    def frozen_command(self) -> tuple[float, ...]:
        cmd = list(self.frozen_task_command)
        cmd[1] = self.hit_plane_x  # configure_online_closd_robot rewrites element 1 to hit_plane_x
        return tuple(cmd)


EMBODIMENTS: dict[str, EmbodimentSpec] = {
    "g1": EmbodimentSpec(
        name="g1",
        num_actions=29,
        sim2sim_module="play_tt_mujoco_g1_fb",
        env_class="G1Env",
        armature_helper=None,
        mjcf="g1/mjcf/g1_full_mesh_with_hand_tt.xml",
        closd_bundle="closd/G1/model_80000.closd-runtime.pt",
        ready_pose="ready_pose/G1/frozen_closd_ready_pose_toeout_lowload.npz",
        policy="policies/g1/policy.onnx",
        paddle_body_name="right_wrist_yaw_link",
        paddle_offset_xyz=(0.18522, 0.033288, 0.089488),
        paddle_offset_rpy=(3.1415926, -0.524, -3.1415926),
        paddle_half_width=0.0763,
        paddle_half_height=0.0763,
        nominal_root_z=0.78,
        hit_x_range=(0.35, 0.35),
        hit_y_range=(-0.76, 0.76),
        hit_z_range=(0.70, 1.30),
        condition_time_range=(0.40, 0.60),
        condition_lateral_range=(-1.10, 0.45),
        condition_forward_range=(-0.05, 0.90),
        condition_height_range=(0.70, 1.12),
        foot_body_names=("left_ankle_roll_link", "right_ankle_roll_link"),
    ),
    "h2": EmbodimentSpec(
        name="h2",
        num_actions=31,
        sim2sim_module="play_tt_mujoco_h2_fb",
        env_class="H2Env",
        armature_helper="build_h2_armature_for_joint_names",
        mjcf="h2/mjcf/h2_pingpong.xml",
        closd_bundle="closd/H2/model_80000.closd-runtime.pt",
        ready_pose="ready_pose/H2/frozen_closd_ready_pose_toeout_lowload.npz",
        policy="policies/h2/h2_stageC_model_10500.onnx",
        paddle_body_name="right_wrist_yaw_link",
        paddle_offset_xyz=(0.19572, 0.036449, 0.08931579),
        paddle_offset_rpy=(0.0, 0.0, 0.0),
        paddle_half_width=0.0815,
        paddle_half_height=0.0855,
        nominal_root_z=1.0,
        hit_x_range=(0.35, 0.45),
        hit_y_range=(-0.76, 0.76),
        hit_z_range=(0.80, 1.50),
        condition_time_range=(0.40, 0.60),
        condition_lateral_range=(-1.35, 0.47),
        condition_forward_range=(-0.08, 1.16),
        condition_height_range=(0.99, 1.45),
        foot_body_names=("left_ankle_pitch_link", "right_ankle_pitch_link"),
    ),
    "a3": EmbodimentSpec(
        name="a3",
        num_actions=29,
        sim2sim_module="play_tt_mujoco_a3_fb",
        env_class="A3Env",
        armature_helper="build_a3_armature_for_joint_names",
        mjcf="a3/mjcf/a3_t2d5_pingpong.xml",
        closd_bundle="closd/A3/model_80000.closd-runtime.pt",
        ready_pose="ready_pose/A3/frozen_closd_ready_pose_toeout_lowload.npz",
        policy="policies/a3/policy.onnx",
        paddle_body_name="right_wrist_yaw_link",
        paddle_offset_xyz=(0.21021, 0.032078, 0.032036),
        paddle_offset_rpy=(0.0, 0.0, 0.0),
        paddle_half_width=0.0763,
        paddle_half_height=0.0763,
        nominal_root_z=1.0,
        hit_x_range=(0.35, 0.45),
        hit_y_range=(-0.76, 0.76),
        hit_z_range=(0.70, 1.40),
        condition_time_range=(0.40, 0.60),
        condition_lateral_range=(-1.35, 0.40),
        condition_forward_range=(-0.10, 1.10),
        condition_height_range=(0.78, 1.26),
        foot_body_names=("left_ankle_roll_link", "right_ankle_roll_link"),
    ),
}


def load_embodiment(name: str) -> EmbodimentSpec:
    key = name.lower()
    if key not in EMBODIMENTS:
        raise KeyError(f"unknown embodiment '{name}', expected one of {sorted(EMBODIMENTS)}")
    return EMBODIMENTS[key]