Repository navigation
IFSSIM v0.2.0 — the vehicle dynamics left Chaos
No prebuilt binaries in this release. Build from source with
./package_mac.sh(macOS) orbash package_windows.sh(Windows) —
see docs/SETUP.md
§2 Option B. Platform zips will be attached here once a self-hosted
runner is available.
The vehicle dynamics left Chaos. This release is dominated by one
thread: the car's physics moved out of Unreal's arcade vehicle
simulator and behind an interface, and a team-authored Simulink model
now drives the car. Alongside that, the autonomy pipeline became a
submodule, every stochastic source became seedable, and a series of
long-standing physics defects were found — several of which had been
silently wrong since the project started.
Two changes are breaking for anyone tracking settings.json or the
repository layout: the autonomy pipeline is no longer in this repo, and
MaxSteerAngle has changed.
Added
IFSDSPlant— the platform/plant seam. The simulator now has an
explicit boundary between the world (terrain, sensors, cones,
referee — Unreal's job) and the vehicle (tyres, suspension,
powertrain, aero — the dynamics engineers' job). SI units, ISO 8855
body frame, ENU world. Two implementations:FFSDSChaosPlant
(default, and still the only validated reference) and
FFSDSFmuPlant.- A Simulink vehicle model, in
matlab/plant/. Six subsystems with
named owners — chassis, tyre/suspension, steering, powertrain, aero,
brakes — each generated from abuild_*.mscript so a regenerated
model is reviewable in a diff rather than an opaque binary. Every
parameter comes fromsettings.json, with per-field provenance
(measured/default/ASSUMPTION). Runs in MATLAB alone; no
Unreal, Docker or ROS needed to work on it. - An FMI 3.0 co-simulation importer. Reads, extracts and gates an
.fmufrom inside the engine, including a ZIP reader written against
zlib because the engine's own only links underbBuildEditor. State
save/restore round-trips bitwise exact. Plant.Typeinsettings.json—chaos,shadoworfmu. In
shadow, the FMU steps alongside Chaos on identical inputs and the
divergence is logged; it drives nothing, so it cannot change
behaviour. Infmuthe FMU integrates the vehicle and the mesh
becomes a kinematic target written from the plant's pose each tick —
sensors already readPlantState, so they follow for free.- The FMU drives the car (Phase 6). Measured: 0 → 21.8 m/s in 10 s
with all four wheels in contact, and a 0.5 steering command giving an
8.3 m radius against 8.22 m fromL/tan(δ)— within 1% of an
independent kinematic prediction rather than a number tuned to match
anything. - A road probe. The platform now answers what is under each wheel
— five rays per wheel, least-squares plane fit, reporting height,
normal and an RMS residual so the plant can detect a bad fit rather
than trust it. Validated against a ramp/crown/step test level with
analytic ground truth, because on flat terrain a working probe and a
stub returning zero produce identical logs. - Seeded determinism.
resetScenarioRPC, a seeded scenario
runner,-fsds.seed=override, and a verifier that proves the RNG
reproduces — which on first run failed, showingresetScenario
alone was insufficient. - Benchmarking toolkit (
tools/sim_benchmark/) — offline
perception/SLAM/control benchmarks, the real C++ EKF driven through
pybind11, bag-based drift checks with frame alignment. - Real-car parity for bag lift — a sim uDV emulator on the stock
Mission Control surface, and auto-derived<name>_carparitybags
(LiDAR + IMU only) for replaying onto the car on stands.
Changed
- Breaking — the autonomy pipeline is now a submodule. Cone
detection, SLAM, planning and control moved to
isc-fs/IFS08-DV-PIPELINE
and are consumed atpipeline/. Pipeline changes go to that repo.
After pulling, rungit submodule update --init --recursive. - Breaking —
MaxSteerAngle28° → 22.4°. Constant-steer sweeps
show lateral acceleration peaks at 22.4° (1.336 g) and falls to
1.268 g by 28°, while yaw/kinematic collapses 0.873 → 0.651. Past the
peak, more lock buys less turn, which inverts the sign of a path
controller's feedback — it runs wide, adds lock, turns less, adds
more. Nothing is lost: every angle removed produced less curvature
than 22.4° already does. The 28° it replaced was never measured. - The sim LiDAR publishes on
/lidar_points, matching the car.
Bags recorded before this need
--remap /lidar/Lidar1:=/lidar_pointson replay. - UE sim time is the authoritative capture clock end-to-end.
Fixed
Most of these had been wrong since the project started, and were found
by building the plant seam rather than by anything failing loudly.
- Chaos was squaring the steering command.
SquaredFunctionwas
the engine default and never overridden, so a 0.5 command produced
0.25 of full lock — the autonomy had been getting roughly half the
steering it asked for in the mid-range, on top of a rate limit
needing 0.4 s to reach full lock. - The Pacejka tyre model never reached the solver. Chaos builds its
physics wheels from the wheel class's class default object before
BeginPlay, so everything written to the per-instance wheels — the
tyre curve, friction, brake torque, radius, steer limit — landed on
an object the solver never reads. Much ofsettings.json's
VehiclePhysicsblock had been decoration. - The tyre curve was far too peaky once it did reach the solver:
LatC 1.9 → 1.4,LatE −1.5 → −0.3. The old shape peaked at 4.9° of
slip and returned 34% of grip at full lock. - Regen never reached the sim. The brake/regen channel was dropped
in the/ctrl/cmdrelay, so the car could only coast, never brake —
the root cause of corner overshoot at speed. - IMU accelerometer noise was 100× too small — applied in cm/s²
while configured in m/s². The EKF had been tuned against a far too
clean IMU. - Spring rate was 2.3× too soft, aero was applied twice, the aero
moment arm was 10× too long, steering ran reverse Ackermann, and the
speed-dependent steering curve was authored in km/h while Chaos
evaluates it in MPH. - Multi-gate tracks spawned the car 90° off (acceleration, skidpad).
- A finished bag could be abandoned when the recorder's stop
service timed out while finalising a multi-GB mcap — the caller read
the timeout as failure and skipped the copy to the host. resetScenarioleft scoring permanently blind — every repeat run
scored 0/0/0.- Plants were initialised twice on the same object. Invisible for
years because the Chaos plant is idempotent; it only became a crash
once something non-reentrant sat behind the same call — the FMU
declares one instance per process, and a secondInstantiate
segfaults rather than failing. Teardown is now deterministic in
EndPlay, since PIE restartsBeginPlayon a new pawn while the old
one is still alive.
Known limitations
- Chaos remains the default and the reference. The FMU drives the
car underPlant.Type="fmu", butchaosis still what a fresh
checkout runs, and it is the implementation every prior lap was
validated against. Same-state parity between the two is ~0.8 m/s²
mean. Crr = 0.020and the 22.4° steering clamp both rest on a
shape-fitted Pacejka, not measured tyre data. Every dynamics number
in this release is internally consistent and none of it is anchored
to the real Hoosier. This is the measurement that would turn a
self-consistent simulator into a validated one.- Four sources still disagree on maximum steering lock — 28°
(invented), 22.4° (tyre peak), 18.2° (uDV firmware), 19.25° (the
IFS-08 workbook). The workbook's figure may derive from the IFS-07
wheelbase; seedocs/IFS_08_measured_parameters.md. Tracked at #462. - The controller normalises steering by 18.2° while the sim maps full
lock to 22.4° — a 1.23× scale mismatch. The clamp makes it safe, not
consistent.