Upstream: https://github.com/pollen-robotics/microduck_rl Upstream-Commit: d424a0c899f6b33cbd3daeb279913134349c0b63 Upstream-Branch: develop
5.1 KiB
Swizzle roller environment — design
Date: 2026-07-23
Branch: new_pre_alpha_rollers
Goal
A separate roller task that produces a clean classic swizzle: both blades
stay on the ground, the legs spread out and pull back in symmetrically
(hourglass pattern), propelling the duck forward. This is a simpler, more stable
alternative to the alternating stride (Mjlab-Velocity-Flat-MicroDuck-Rollers),
motivated by the stride not transferring well to the real robot. The stride env is
left untouched.
Sim2real is a target: same robot, observations, command semantics, domain
randomization and ONNX export as the stride env, so it deploys identically
(microduck_runtime ... --roller, same flags).
Approach (chosen: A — remove anti-swizzle + reward symmetry)
The base roller velocity recipe naturally converges to a swizzle (this is the attractor we fought against for the stride). So the simplest way to a clean swizzle is to remove the anti-swizzle machinery and reward the swizzle's defining features (symmetry, feet grounded). No phase scripting.
Rejected: B (explicit hourglass foot-pattern shaping) and C (phase-driven scripted trajectory) — more complex, only needed if A's swizzle looks messy (rhythm/amplitude).
Structure
- New file
src/mjlab_microduck/tasks/microduck_velocity_swizzle_env_cfg.pywithmake_microduck_velocity_swizzle_env_cfg(play=False)andMicroduckSwizzleRlCfg. Built frommake_velocity_env_cfg()+ the roller robot, mirroringmicroduck_velocity_rollers_env_cfg.py's structure (obs, DR, command, curricula). - Register
Mjlab-Velocity-Swizzle-MicroDuckintasks/__init__.py. - Reuse everything sim2real from the stride env: robot cfg, 61D obs layout, command
(cmd_x push/coast/brake, straight-line:
ang_vel_z=(0,0),heading_hold), all DR events + curricula (com, wheel_friction),action_over_limit, ONNX export path.
Reward recipe
Kept (task + stability + sim2real):
wheel_speed (forward propulsion, the task), braking, upright, com_height_target,
pose, forward_lean, heading_hold, action_over_limit, feet_flat,
self_collisions, regularizers (action_rate_l2 + curriculum, neck_action_rate_l2,
neck_joint_pos_l2, joint_torques_l2).
Removed (stride / anti-swizzle machinery):
single_support, glide, skating_air_time, gait_symmetry, hip_roll_neutral
(the last would fight the swizzle's lateral out-motion).
Added (pro-swizzle):
leg_symmetry— reward left/right legs mirroring. The robot uses mirrored L/R sign conventions, so a symmetric config satisfiesq_left + q_right ≈ 0per pair. Return-mean_pairs |q_left + q_right|(L1, constant gradient — same form as the existingbilateral_symmetry_penalty) over the leg joint pairs (hip_yaw, hip_roll, hip_pitch, knee, ankle); used with a positive weight so asymmetry is penalised and the symmetric swizzle is favoured. This is the swizzle's defining feature. (Implementation: the existingbilateral_symmetry_penaltytakes explicit L/R index lists; add a thin wrapper that resolves the L/R leg-joint pairs by name at runtime so it can be configured without hard-coded indices.)grounded— reward both blades in contact (n_contact == 2) while pushing, so the feet stay down (classic swizzle, no lifting). Small weight. New mdp function (mirror ofsingle_support_rewardbut rewarding double support). Gate oncmd_x >= 0like the others.
Leave hip_roll pose std loose (as in the stride env) so the legs can spread.
New mdp functions (in tasks/mdp.py)
leg_symmetry_reward(env, asset_cfg)— resolve L/R leg joint pairs by name, return-mean_pairs |q_left + q_right|(used with a positive weight).grounded_reward(env, sensor_name, command_name)— reward exactly-two-blades in contact, scaled byclamp(cmd_x, 0).
Command / sim2real (identical to stride)
cmd_x push/coast/brake, lin_vel_y=0, ang_vel_z=(0,0) (straight-line), full DR
(com, head_com, mass/inertia, joint friction, armature, wheel friction, velocity
pushes, IMU misalignment, encoder bias, obs delays), 61D obs, vel_scale=0.3.
Deploys with the same runtime flags as the stride roller policy.
PPO config
Reuse MicroduckRollersRlCfg's hyperparameters (same actor/critic 512-256-128 ELU,
PPO settings, entropy_coef=0.03), new experiment_name/run_name = velocity_swizzle.
Testing / verification
- Smoke test:
uv run train Mjlab-Velocity-Swizzle-MicroDuck --env.scene.num-envs 16 --agent.max-iterations 2runs without error;leg_symmetryandgroundedappear in the reward log. - Watch on a real run:
leg_symmetryhigh (symmetric),groundedhigh (both feet down),wheel_speedrising (moves forward). Video: symmetric hourglass swizzle, both blades on the ground.
Tuning knobs (post-first-run)
- If not symmetric enough → raise
leg_symmetryweight. - If it lifts feet → raise
groundedweight. - If it barely moves → the symmetry/grounded weights are too high vs
wheel_speed; lower them. - If the swizzle looks messy (rhythm/amplitude) → escalate to Approach B.