Skip to content

Add MuJoCo backend for racecar_core Drive/Lidar interface - #27

Merged
Aeh961 merged 6 commits into
mainfrom
aeh961/racecar-core-adapter
Aug 16, 2026
Merged

Add MuJoCo backend for racecar_core Drive/Lidar interface#27
Aeh961 merged 6 commits into
mainfrom
aeh961/racecar-core-adapter

Conversation

@Aeh961

@Aeh961 Aeh961 commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Implements a thin MuJoCo adapter compatible with the racecar_core Drive and LiDAR interfaces.

  • Maps normalized speed/steering commands to MuJoCo actuators
  • Converts the MuJoCo 8-beam LiDAR scan to racecar_core conventions
  • Handles steering sign conversion
  • Adds 20 focused validation tests
  • Verified 74/74 validation tests pass
  • Ruff check and format checks pass
  • Confirmed the existing ultimate-wall-follower controller can run unmodified against the adapter

This is intended to establish the shared controller interface between MuJoCo, Playground, and the physical car, and to unblock the Gymnasium environment work in #7.

Closes #8

Comment thread src/neoracer_mujoco/racecar_core_adapter.py Outdated
Comment thread src/neoracer_mujoco/racecar_core_adapter.py
Comment thread src/neoracer_mujoco/racecar_core_adapter.py Outdated
Comment thread src/neoracer_mujoco/racecar_core_adapter.py
Comment thread validation/test_racecar_core_adapter.py Outdated
Comment thread validation/test_racecar_core_adapter.py
Comment thread src/neoracer_mujoco/racecar_core_adapter.py
Abdallahelhamawi and others added 2 commits August 15, 2026 16:45
Add MujocoController, MujocoDisplay, and MujocoRacecar to
racecar_core_adapter.py, wiring the existing MujocoDrive/MujocoLidar into a
start/update/update_slow/go event loop that mirrors racecar_core.Racecar's
own contract. This lets an unmodified racecar_core controller script (e.g.
neoracer-labs/ultimate-wall-follower/wall_follower.py) run against MuJoCo
with only its `import racecar_core` needing to resolve to this class.

Vendor a byte-for-byte copy of the canonical wall-following example under
validation/testdata/ (see SOURCE.md for provenance) and add a pytest
acceptance test proving it runs unmodified against MujocoRacecar via a
sys.modules shim.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add examples/run_racecar_core.py, a process-level entry point that runs an
unmodified racecar_core controller file against NeoRacer MuJoCo outside
pytest: it composes and settles a walled-corridor scene, builds a
MujocoRacecar, injects a fake racecar_core module into sys.modules whose
create_racecar() returns it, puts the controller's own directory on
sys.path for sibling imports, and executes the controller unchanged via
runpy.run_path() so its own __main__ block drives the go() loop.

Vendor the canonical wall-following example under
examples/ultimate_wall_follower/ (byte-for-byte copy of
validation/testdata/ultimate_wall_follower/, verified in
test_run_racecar_core.py) as the runnable, drag-and-drop demo target for
the new bootstrap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Aeh961

Aeh961 commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Tier A racecar_core compatibility — complete

Two commits landed on this branch, on top of the original Drive/Lidar work:

  • c8eb3e7feat: add Tier A racecar_core compatibility adapter
  • 83014a8feat: add runnable racecar_core controller bootstrap

What's new

NeoRacer now provides Tier A compatibility for the RACECAR API through:

  • MujocoRacecar — the top-level object, wiring the others together and implementing the start/update/update_slow/go event loop
  • MujocoDrive
  • MujocoLidar
  • MujocoController
  • MujocoDisplay

The canonical ultimate_wall_follower controller runs unchanged against this adapter. wall_follower.py, control.py, and config/wall_follower.yaml remain byte-identical to the canonical vendored fixture (verified by sha256 comparison and by validation/test_run_racecar_core.py).

Production runtime bootstrap

A real, non-pytest entry point now exists at:

examples/run_racecar_core.py

Run the canonical example with:

python3 examples/run_racecar_core.py examples/ultimate_wall_follower/wall_follower.py

Actual runtime validation of that exact command:

  • Exit code 0
  • start(), update(), and update_slow() are all exercised
  • Drive and steering commands reach the MuJoCo adapter
  • The car moved ~5.49 meters during the default 600-step validation run
  • No external racecar_core installation is required — the compatibility shim (a fake racecar_core module whose create_racecar() returns a MujocoRacecar) is injected into sys.modules only inside the runner process; it does not modify site-packages and does not globally shadow a real racecar_core install
  • The default command is headless — no MuJoCo GUI viewer is opened by this command

Validation

pytest validation/ -v
84 passed

ruff check .
All checks passed

Known scope boundary

Tier A currently covers the RACECAR APIs this controller actually needs (drive, lidar, controller, display). Other RACECAR subsystems — camera, LED, navigation, SLAM, telemetry, vision, and other modules — remain outside the verified compatibility surface unless already implemented elsewhere.

…ts tests

Local checks had run against a stale 0.15.21 ruff in the dev conda env;
CI (and requirements.txt's pinned ruff==0.16.0) caught RUF059 (unused
unpacked variable), BLE001 (blind except, now justified with noqa at the
CLI's top-level error handler), RUF100 (stale noqa), PLW1510 (subprocess.run
without explicit check=), and formatting. No behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Aeh961

Aeh961 commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up: pushed 4a2126b to fix 5 CI-only ruff findings (RUF059, BLE001, RUF100, PLW1510, formatting) surfaced by the pinned ruff==0.16.0 in CI that a stale local ruff 0.15.21 had missed. No behavior change. CI ruff check is now passing on this branch.

- Trim the module docstring to what the adapter does and the two
  intentional sign/unit conventions it bridges (Thread 1).
- Remove the unnecessary sensors-as-_sensors alias in
  racecar_core_adapter.py: it exists in sim.py because that module
  defines its own public sensors() function, but no such collision
  exists here (Thread 3).
- Inline the _fresh(car) test helper -- all ~18 call sites now
  construct mujoco.MjData(car) directly (Thread 5).
- Add a brief validation note to test_wall_follower_example.py
  documenting the additional steering-sign/LiDAR verification, and
  explicitly scoping the claim to compatibility, not autonomous
  wall-following success.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Aeh961
Aeh961 merged commit 9fe3b8a into main Aug 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add controller input support

3 participants