Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ jobs:
# bindings must be built or every one of them fails with
# ModuleNotFoundError. pybind11 is pinned: it decides how the module is
# compiled, so an unpinned bump is a silent toolchain change.
# gymnasium is pinned for ctest rl_env (vdsim_rl); a missing import fails.
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install "pybind11==2.13.6" numpy pyyaml matplotlib pillow
python3 -m pip install "pybind11==2.13.6" numpy pyyaml matplotlib pillow "gymnasium==0.29.1"

- name: Report toolchain
run: |
Expand Down Expand Up @@ -121,7 +122,7 @@ jobs:
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install "pybind11==2.13.6" numpy pyyaml matplotlib pillow
python3 -m pip install "pybind11==2.13.6" numpy pyyaml matplotlib pillow "gymnasium==0.29.1"

- name: Pin CMake
# CMakePresets.json is version 3, which needs CMake >= 3.21, so the
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
CIBW_TEST_COMMAND: >-
python -c "import vdsim; print(vdsim.__file__)" &&
python -c "from vdsim_lab import Sim; Sim(vehicle='sedan', level='L2'); print('sedan ok')" &&
python -c "from vdsim_lab import Sim; Sim(vehicle='ioniq5_awd', tire='ioniq5_pac2002', level='L2'); print('ioniq5 ok')" &&
python -c "from vdsim_lab import Sim; Sim(vehicle='generic_sedan', tire='generic_pacejka', level='L2'); print('generic ok')" &&
python "{project}/tools/wheel_import_smoke.py"
- uses: actions/upload-artifact@v4
with:
Expand Down
42 changes: 42 additions & 0 deletions configs/parts/tire/generic_pacejka.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generic Pacejka MF96 + LuGre tyre -- the values core/src/params.cpp compiles
# in as the TireParams() defaults, in the flat TireParams shape.
#
# Source: vdsim.TireParams().to_yaml(), 2026-09-22. Not measured data.
# configs/parts/tire/default_pacejka.yaml carries the same tyre in the catalog
# part schema (id/type/schema/body); vdsim_rl reads flat files only and refuses
# catalog parts by design, so the generic car needs this flat copy.
# tests/rl/test_rl_env.py (Q23-f1) fails if these drift from the C++ defaults.
B_long: 10.0
C_long: 1.65
D_long: 1.0
E_long: 0.97
B_lat: 8.0
C_lat: 1.3
D_lat: 1.0
E_lat: -1.0

mu_nominal: 1.0
Fz_nominal: 4000.0
cornering_stiffness: 80000.0
rolling_resistance: 0.0
combined_slip_enabled: true
pneumatic_trail: 0.05
trail_falloff_alpha: 0.2
camber_stiffness: 0.0
load_sensitivity: 0.0
relaxation_length_lat: 0.0
relaxation_length_long: 0.0
tire_vertical_stiffness: 220000.0
reff_breff: 0.0
reff_dreff: 0.0
reff_freff: 0.0
crown_radius: 0.0
vlow_speed_eps: 0.15
low_speed_mode: kinematic_blend

lugre:
enabled: true
sigma0: 90000.0
sigma1: 0.0
sigma2: 75.0
m_eff: 40.0
12 changes: 12 additions & 0 deletions configs/rl/default_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
# Observation layout is this list, left to right; a bare per-wheel name expands
# to four columns in FL, FR, RL, RR order.
level: L2
# car: configs/vehicles/<stem>.yaml + configs/parts/tire/<stem>.yaml.
# generic_sedan + generic_pacejka are the C++ built-in defaults written out
# as files, so a run records the car it used instead of leaving it unnamed
# (PO decision 78). G1 substep accuracy at the 1 ms below, same table as
# fast_env.yaml (a): ay 0.55 %, yaw 0.20 %, Fz_FL 1.22 %.
#
# To run a car whose parameters are not in this repository, put it under
# $VDSIM_PRIVATE_CONFIGS/vehicles/<stem>.yaml (tyre under
# $VDSIM_PRIVATE_CONFIGS/parts/tire/) and name the stem here, or pass an
# absolute vehicle_file / tire_file. reset() then reports source=private.
vehicle: generic_sedan
tire: generic_pacejka
dt: 0.005
action_repeat: 4 # control interval = 20 ms
max_steer: 0.5 # [rad] at the wheel
Expand Down
56 changes: 48 additions & 8 deletions configs/rl/fast_env.yaml
Original file line number Diff line number Diff line change
@@ -1,23 +1,63 @@
# vdsim_rl training preset: same plant as default_env.yaml, coarser substep.
# vdsim_rl training preset: fast prototyping on the C++ built-in generic car at
# a coarser substep. default_env.yaml keeps the same car at 1 ms.
#
# G1 gate (2026-09-17, L3, outer tick 5 ms, reference = same plant at
# max_substep_dt 0.1 ms). Worst max-relative error over the manoeuvre,
# throughput = 64 envs on 22 threads of an i7-13700K:
# G1 gate, outer tick 5 ms, reference = same plant at a finer max_substep_dt
# (0.1 ms for the rows marked [ref 0.1 ms], 1 us for [ref 1 us]).
# Worst max-relative error over the manoeuvre, throughput = 64 envs on 22
# threads of an i7-13700K. Reproduce: tests/rl/gate_g1_substep_accuracy.py.
# Criterion fixed before the measurement: ay and yaw <= 2 %, Fz_FL <= 5 %.
#
# (a) C++ built-in generic car (= configs/vehicles/generic_sedan.yaml), L3,
# 2026-09-17. This is the table the 2.5 ms choice below was made on.
#
# substep step-steer ay step-steer yaw brake Fz_FL throughput
# 0.50 ms 0.23 % 0.08 % 0.51 % 80 k steps/s
# 1.00 ms 0.52 % 0.18 % 1.22 % 157 k steps/s <- default_env.yaml
# 2.50 ms 1.38 % 0.37 % 4.07 % 377 k steps/s <- this file
# 5.00 ms 6.82 % 0.82 % 6.69 % 722 k steps/s (rejected)
#
# Adopted: 2.5 ms for training (2.4x throughput, trajectory signals within
# 1.4 %). Keep default_env.yaml (1 ms) for evaluation and for any reward that
# reads per-wheel load - Fz is the signal that degrades first. 5 ms is not
# adopted: ay and Fz both pass 6 %.
# Same car at L2 (this file's level), 2026-09-22, same script:
# 2.50 ms 1.45 % 0.53 % 4.07 % 326 k steps/s <- this file
# 1.00 ms 0.55 % 0.20 % 1.22 % 131 k steps/s
# (not in the criterion: brake pitch 30.6 % and step-steer roll 8.4 % at
# 2.5 ms, 9.2 % / 1.5 % at 1 ms - do not build a reward on pitch or roll.)
#
# Rows above are [ref 0.1 ms]. Same car, L2, same manoeuvres and error
# definition, reference re-integrated at 1 us (Q23-h, 2026-09-22,
# solver_substep_clamp_count() == 0) [ref 1 us]:
# substep step-steer ay step-steer yaw brake Fz_FL pitch roll
# 0.01 ms 0.005 % 0.002 % 0.010 % 0.08 % 0.01 %
# 0.10 ms 0.059 % 0.022 % 0.117 % 0.88 % 0.16 %
# 1.00 ms 0.61 % 0.22 % 1.34 % 10.1 % 1.7 %
# 2.50 ms 1.51 % 0.55 % 4.19 % 31.5 % 8.5 % <- this file
# Observed order p = log10(E(h)/E(h/10)) is 1.01-1.06 between 1 and
# 0.1 ms and 1.04-1.05 between 0.1 and 0.01 ms (first order, monotone),
# so the 1 us reference is sound and the old 0.1 ms reference was
# itself off by the 0.10 ms row: the [ref 0.1 ms] rows read about 3-11 %
# (relative) too optimistic. The 2.5 ms row still passes the criterion.
#
# Adopted (PO decision 69, 2026-09-22): 2.5 ms for training - 2.4x the
# throughput of 1 ms with trajectory signals within 1.5 % and Fz_FL 4.07 %.
# Keep default_env.yaml (1 ms) for evaluation and for any reward that reads
# per-wheel load - Fz is the signal that degrades first. 5 ms is not adopted:
# ay and Fz both pass 6 %.
#
# Both RL declarations name the generic car explicitly since PO decision 78:
# configs/vehicles/generic_sedan.yaml carries the same values the C++ defaults
# compile in, so table (a) describes both files.
#
# A substep is a property of the car, not of the solver. Presets whose specs
# are not public are selected by the same keys but injected from outside the
# repository ($VDSIM_PRIVATE_CONFIGS); their G1 tables are kept with them, not
# here. Re-run the script above for any preset you swap in - do not carry this
# file's 2.5 ms over to it.
#
# max_substeps must stay above outer_dt / max_substep_dt, otherwise the solver
# silently clamps and runs a coarser step than requested.
level: L2
# car: the generic car of table (a), named rather than left unset.
vehicle: generic_sedan
tire: generic_pacejka
dt: 0.005
action_repeat: 4
max_steer: 0.5
Expand Down
65 changes: 65 additions & 0 deletions configs/vehicles/generic_sedan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Generic mid-size sedan -- the values core/src/params.cpp compiles in as the
# VehicleParams() defaults, written out so a run can name the car it used
# instead of leaving `vehicle` unset.
#
# Source: vdsim.VehicleParams().to_yaml() (core/src/params.cpp
# VehicleParams::to_yaml), 2026-09-22. This is a solver default set, not a
# measured vehicle: do not cite any number here as a specification of any real
# car. tests/rl/test_rl_env.py (Q23-f1) compares every bound field of the
# loaded params against vdsim.VehicleParams() and fails if the two drift apart.
mass: 1500.0
mass_sprung: 1350.0
inertia_diag: [500.0, 2000.0, 2500.0]
wheelbase: 2.7
cg_to_front: 1.2
cg_to_rear: 1.5
track_front: 1.55
track_rear: 1.55
cg_height: 0.55
wheel_radius_nominal: 0.32

spring_stiffness: [30000.0, 30000.0, 30000.0, 30000.0]
damper_coefficient: [3000.0, 3000.0, 3000.0, 3000.0]
unsprung_mass: [40.0, 40.0, 40.0, 40.0]
wheel_inertia: [0.0, 0.0, 0.0, 0.0] # 0 = core derives a disc from radius/mass
arb_stiffness_front: 0.0
arb_stiffness_rear: 0.0
roll_center_height_front: 0.0
roll_center_height_rear: 0.0
anti_dive_front: 0.0
anti_squat_rear: 0.0
camber_per_roll: 0.0

drive_type: RWD
drive_split_front: 0.5
differential: Open
lsd_preload: 0.1
lsd_ramp: 0.2
plant_path: false

max_motor_torque: 300.0
final_drive_ratio: 5.0
engine_rotational_inertia: 0.25
max_brake_torque: 2000.0
brake_bias_front: 0.5
brake_ebd_enabled: false
brake_deadtime_s: 0.0
drive_deadtime_s: 0.0
steer_deadtime_s: 0.0

steering_ratio: 15.0
max_steer_angle_wheel: 0.5
ackerman_percent: 0.0

aero_drag_coeff: 0.3
frontal_area: 2.2
aero_lift_front: 0.0
aero_lift_rear: 0.0

tire_yaml: parts/tire/generic_pacejka.yaml

# Drawn-primitive sizes for the 3D replay renderer (trace schema 0.3 geometry).
# Placeholder box for a generic sedan, no source, not a specification; no
# equation reads them.
body_lwh_m: [4.6, 1.8, 1.45]
wheel_width_m: 0.225
10 changes: 10 additions & 0 deletions cosim/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ if(Python3_FOUND)
set_tests_properties(vla_plant_demo PROPERTIES
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
ENVIRONMENT "VDSIM_BUILD_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_BINARY_DIR}/python:${CMAKE_SOURCE_DIR}/python")
# vdsim_rl: torch-free checks only (gymnasium API, termination codes, obs
# buffer, vehicle preset input). A missing gymnasium fails the test; the
# SB3 PPO smoke (tests/rl/smoke_sb3_ppo.py) stays outside ctest.
add_test(NAME rl_env
COMMAND ${Python3_EXECUTABLE}
${CMAKE_SOURCE_DIR}/tests/rl/test_rl_env.py)
set_tests_properties(rl_env PROPERTIES
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
TIMEOUT 300
ENVIRONMENT "VDSIM_BUILD_DIR=${CMAKE_BINARY_DIR};VDSIM_BUILD_PY=${CMAKE_BINARY_DIR}/python;PYTHONPATH=${CMAKE_BINARY_DIR}/python:${CMAKE_SOURCE_DIR}/python")
add_test(NAME control_ladder
COMMAND ${Python3_EXECUTABLE}
${CMAKE_SOURCE_DIR}/examples/control_ladder_demo.py)
Expand Down
Loading
Loading