Replace the failing ODE-rolled mecanum chassis dynamics with a Supervisor.setVelocity call that uses the gym mecanum forward kinematics formula directly. Wheel motors still spin (visual); chassis motion comes from the gym model so training and deployment match by construction. Results (seed=42, n=10 sheep): BC + RL mecanum pen 10/10 in both field and field_round. n=5 mecanum cells still 0/5 due to tracker phantoms anchored to wall corners under the 360° LiDAR — documented in docs/status.md as the remaining gap. Cleanup: drop deploy-time hacks (HERDING_HEADING_*, HERDING_OMEGA_CLAMP, HERDING_TRACKER_*) that were workarounds for the old ODE chaos; revert the proto inertiaMatrix, roller dampingConstant, and reduced motor torque since they no longer carry load; refresh comments around the mecanum config presets.
5.0 KiB
Status — 2026-05-18
Current snapshot of what works in Webots, and what design choices got us here.
Results matrix (Webots, seed=42)
Differential drive — bash tools/run_webots.sh N MODE differential WORLD:
| controller | field n=5 | field n=10 | field_round n=5 | field_round n=10 |
|---|---|---|---|---|
| BC | 5/5 | 10/10 | 5/5 | 10/10 |
| RL | 5/5 | 10/10 | 5/5 | 10/10 |
| Strömbom | 5/5 | 10/10 | 5/5 | 10/10 |
| Sequential | 5/5 | 10/10 | 5/5 | 10/10 |
Mecanum drive — bash tools/run_webots.sh N MODE mecanum WORLD HERDING_LIDAR=360:
| controller | field n=5 | field n=10 | field_round n=5 | field_round n=10 |
|---|---|---|---|---|
| BC | 0/5 | 10/10 | 0/5 | 10/10 |
| RL | 0/5 | 10/10 | 0/5 | 10/10 |
Extra-merit:
- 360° LiDAR ablation —
HERDING_LIDAR=360works in all four diff cells. - Dual-dog axis-split —
HERDING_NDOGS=2 HERDING_AXIS_LEAK=0.3pens 5/5 on diff.
Architecture decisions and why
Differential drive — full ODE simulation
Standard Webots physics with two wheel motors and a caster. No special handling needed; the chassis is dynamically stable, and the trained policies transfer directly to Webots.
Mecanum drive — kinematic Supervisor injection
The mecanum proto uses physical 8-roller wheels for visual fidelity, but the chassis is moved by Supervisor.setVelocity() using the gym mecanum forward-kinematics formula (see controllers/shepherd_dog/shepherd_dog.py::drive_mecanum).
We explored two other paths before settling here:
- Plain cylinder wheels + anisotropic ContactProperties. Tried
frictionRotation ±0.7854on the wheel contact frame. Strafe motion came out the wrong direction and diagonals zeroed out. Discarded. - Full ODE simulation on 32 physical roller hinges. The free-spinning rollers coupled chaotically through the body, producing ±150° yaw drift over 200 control steps. Even with
inertiaMatrixoverrides,dampingConstanton every roller, and a 6× cut to motor torque, dynamic policy commands kept producing tumbling. Discarded. - Kinematic Supervisor injection (current). ODE physics on the wheels is kept for visuals only; the chassis velocity is set directly each step from the gym forward-kinematics formula. Gym training and Webots deployment produce identical body motion. Yaw drift is zero by construction.
This is not a hack — it matches how most academic mecanum sims work (e.g., Gazebo's mecanum plugins use kinematic models by default; ODE's contact solver does not handle the rolling-without-slipping constraint cleanly for 32 free hinges).
Why n=5 mecanum fails (and n=10 passes)
The 360° LiDAR scans the full perimeter every step. Wall corners, gate posts, and pen rails occasionally produce sheep-shaped blobs that pass the wall_reject and static_reject filters. The tracker promotes a candidate to "active" after consensus_k=3 consistent hits within 20 steps — phantoms anchored to fixed world features satisfy this trivially.
With n=10 real sheep, the tracker's active slots fill with real sheep and phantoms can't compete. With n=5 there are ~5 free slots that wall phantoms occupy; the policy then chases ghosts.
Tightening the consensus filter (consensus_k=5) and wall_reject=0.9 were tried; both kept ~70% of frames at 10 active tracks. The proper fix is parallax-aware tracking — record each track's world position across multiple dog vantage points; real sheep move, static phantoms don't. Out of scope for the 2026-06-04 deadline.
File map (what changed in this push)
herding/config.py mecanum presets keep matched
strafe scaling (strafe_eff=0.26,
bleed=-0.40) for kinematic injection
controllers/shepherd_dog/shepherd_dog.py
Supervisor() + drive_mecanum kinematic
injection via _self_node.setVelocity
protos/ShepherdDogMecanum.proto supervisor TRUE; physics tuning
protos/ShepherdDogMecanum360.proto reverted (ODE no longer load-bearing)
tools/gen_mecanum_wheels.py wheels regen-script (clean)
tools/run_webots.sh contact-properties comment cleaned
training/{bc/collect,rl/train}.py comment cleanup; preset selection unchanged
Options for the remaining cleanup
- Keep matched preset (0.26, -0.40). Policies trained against these values; controller applies them at deploy; no retrain. Current state.
- Switch preset to textbook (1.0, 0.0) and retrain mecanum BC+RL (~6h). Cleaner story (textbook mecanum throughout); same kinematic-injection mechanism.
Either is defensible. (1) ships faster; (2) is more "pure".