microduck-simulator / app /src /game /constants.js
tfrere's picture
tfrere HF Staff
feat: comic-panel HUD, runtime-faithful pad mapping, props + wheee voices
89f5399
Raw
History Blame Contribute Delete
6.2 kB
// Shared sim constants, lifted verbatim from the pre-React rl.js.
export const POLICY_DIR = "./policies";
export const POLICIES = {
walk: `${POLICY_DIR}/BEST_alpha_walking.onnx`,
sitstand: `${POLICY_DIR}/BEST_alpha_sitstand.onnx`,
roll: `${POLICY_DIR}/roulade.onnx`,
// Blind one-shot kicks (the operator aims the robot, no ball in obs):
// the runtime swaps these in for a 0.5 s window, commands zeroed.
kickL: `${POLICY_DIR}/ball_kick_left.onnx`,
kickR: `${POLICY_DIR}/ball_kick_right.onnx`,
// One-shot ground pick (runtime A button): peck the ground and stand
// back up, phase-driven via [cos, sin, 0] in the command vel slots.
groundpick: `${POLICY_DIR}/alpha_ground_pick.onnx`,
// Get-up policy for the automatic fall recovery (runtime --fall-detect):
// same 61D obs layout as the other alpha policies, commands all zeroed.
stand: `${POLICY_DIR}/BEST_alpha_stand.onnx`,
// Roller variant (lazy-loaded on first switch, never at boot):
// drive = velocity-tracking skating, crouch = one-shot crouch-glide
// driven by a phase encoding in the command slots (ground-pick style).
drive: `${POLICY_DIR}/BEST_roller.onnx`,
crouch: `${POLICY_DIR}/BEST_roller_crouch.onnx`,
};
// From the ONNX metadata (identical for all alpha policies) and the STAND
// keyframe in mjlab's scene_walk.xml. Order matches the actuators in
// the MJCF.
export const JOINT_NAMES = [
"left_hip_yaw", "left_hip_roll", "left_hip_pitch", "left_knee", "left_ankle",
"neck_pitch", "head_pitch", "head_yaw", "head_roll",
"right_hip_yaw", "right_hip_roll", "right_hip_pitch", "right_knee", "right_ankle",
];
export const DEFAULT_POSE = new Float32Array([
0, -0.08726646259971647, -0.457924, -0.004940, 0.452984,
0.3490658503988659, 0.3490658503988659, 0, 0,
0, 0.08726646259971647, 0.457924, 0.004940, -0.452984,
]);
export const NUM_JOINTS = 14;
export const OBS_SIZE = 61;
export const CMD_SIZE = 13;
export const ACTION_SCALE = 1.0;
export const TIMESTEP = 0.005;
export const DECIMATION = 4;
export const CTRL_DT = TIMESTEP * DECIMATION; // 50 Hz
// Velocity command limits, same as infer_policy.py's keyboard mapping.
// No strafe input anymore: the lateral cmd slot stays zeroed for the obs.
export const VEL_FWD = 0.25, VEL_BACK = -0.2, VEL_ANG = 1.0;
// Roller mode limits, from the runtime's roller branch: asymmetric vx
// (0.6 push / 0.5 brake), no lateral. The real runtime launches rollers
// with --max-angular-vel 0.3: faster commanded turns tip the robot over,
// so the playground clamps wz the same way.
export const RVEL_FWD = 0.6, RVEL_BACK = -0.5, RVEL_ANG = 0.3;
// Crouch-glide one-shot: command = [cos(2pi*phase), sin(2pi*phase), 0],
// phase advancing at 1/CROUCH_PERIOD_S per second and the cycle exiting
// at 0.7 - exactly the runtime's ground-pick slot the policy was trained
// against (mjlab CROUCH_PERIOD = 5.0, cycle end 0.7 => 3.5 s gesture).
export const CROUCH_PERIOD_S = 5.0;
export const CROUCH_END_PHASE = 0.7;
// Ground-pick one-shot (legs): same phase encoding, from the runtime's
// defaults (--ground-pick-period 4.0, cycle exiting at 0.7 => ~2.8 s
// gesture, action scale and kP untouched at their 1.0 defaults).
export const GROUND_PICK_PERIOD_S = 4.0;
export const GROUND_PICK_END_PHASE = 0.7;
// Kickable ball: radius and parking spot (far away = hidden by default).
export const BALL_RADIUS = 0.05;
export const BALL_PARK_POS = "50 0 0.05";
// Square arena boxing the play area: static walls at +-ARENA_HALF keep
// the ball (and the duck) inside. Tall enough that neither steps over.
export const ARENA_HALF = 1.5; // inner half-size, m
export const ARENA_WALL_H = 0.25;
export const ARENA_WALL_T = 0.05;
// Section grid: 5 cells across the 3 m arena (ODD, so a true middle
// column/row of cells exists; the lattice is shifted half a cell in the
// shaders so the walls land exactly on section lines).
export const GRID_SECTION = (2 * ARENA_HALF) / 5; // 0.6 m
// Arcade cabinet row: three cabinets side by side against the front (+X)
// wall, screens facing the arena. Consumed by the prop library (props.js
// "arcade" def - currently benched, enabled: false) for both the clone
// placements and the row's single static collision box. Proportions
// measured from the GLB's natural size (0.524 x 0.587 x 1.0 m, w x d x h).
// Slightly surreal scale: ~2.8x the 0.25 m duck (a real 1.73 m cabinet
// felt overwhelming in the 3 m arena). Big enough to read as oversized
// furniture, small enough that the row (~1.15 m wide, 0.41 m deep)
// leaves the play area open.
export const ARCADE_H = 0.7; // target height, m
export const ARCADE_W = ARCADE_H * 0.524; // footprint width, ~0.22 m
export const ARCADE_D = ARCADE_H * 0.5876; // footprint depth, ~0.247 m
export const ARCADE_GAP = 0.02; // gap between neighbouring cabinets
export const ARCADE_WALL_GAP = 0.01; // clearance between backs and the wall
// Relief (prototype): gentle cosine bumps baked into the level itself.
// One shared analytic height function drives BOTH the physics (a MuJoCo
// heightfield covering the arena) and the visuals (vertex displacement
// of the grid floor shader), so the grid genuinely deforms instead of
// boxes popping out of it. Entries are [cx, cy, height, radius] in MJCF
// coords; slopes stay mild (max grade ~20%) so the blind walking policy
// has a chance, and the spawn cell + arcade row stay flat.
export const RELIEF_BUMPS = [
[0.3, -0.9, 0.06, 0.55],
[-0.9, 0.75, 0.05, 0.5],
[0.6, 0.9, 0.07, 0.55],
];
export const RELIEF_HMAX = 0.07; // tallest bump, scales the hfield z-size
export const RELIEF_GRID = 65; // hfield rows/cols over the 3x3 m arena
export const RELIEF_SINK = 0.003; // hfield geom buried below the floor
export const RELIEF_RATE = 0.6; // raise/sink rate, scale units per second
// Spawn: center of the middle section cell in the SECOND ROW FROM THE
// BACK wall. The duck faces +X (identity freejoint quat, walks toward
// local +X), so "back" is the -X wall: row centers sit at x = -1.2,
// -0.6, 0, 0.6, 1.2 -> second row is -0.6; middle column is y = 0.
// MJCF coordinates (three.js: x -> x, y -> -z).
export const SPAWN_X = -ARENA_HALF + 1.5 * GRID_SECTION; // -0.6
export const SPAWN_Y = 0;