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
35 changes: 35 additions & 0 deletions docs/roadmap/NAV-VEHICLE-PHYSICS-ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,41 @@ The plan, in order:
trained on the dynamic model beats the point-mass one over paired seeds.
4. **P8c:** a differentiable power model and energy in the loss, calibrated to the P3 ledger.

### P8a's vehicle fitted to Jolt (2026-10-20)

`nav_plant::dyn_bike_si_of` now gives the dynamic model the values the Jolt plant's one-tick
residual asks for. Jolt runs of the Humvee and the RoboRacer were fitted on held-out seeds, which
made the twist residual about 11x lower.

- **Tires.** Jolt's lateral friction ramps linearly to its peak at `tire_peak_slip_angle`: a
cornering stiffness of 1 / alpha_peak per unit load. A Pacejka tire peaking at the same angle
starts 4.5x steeper. B is now set so that B C = 1.12 / alpha_peak (`kJoltTireStiffness`).
- **Front and rear.** They are alike now (`front_stiffness` 1, was 0.85), since Jolt's tires have no
load sensitivity.
- **A direct drive** (one gear, no torque converter: the RoboRacer) gets no drive lag and no drag or
rolling resistance. The driver model's integral trim tracks the commanded acceleration, so the
command is the achieved acceleration.
- **A geared drive through a converter** (the Humvee) keeps its 0.1 s lag and its resistances.

The P8b gate rerun on Jolt RoboRacers (city, 20 held-out seeds, paired bootstrap; Linux), before
and after:

| training | dynamic-trained, before / after | dynamic - untrained seed, before | after |
|---|---|---|---|
| lr 5e-5, 150 steps | 50.8% / 51.6% | +0.2 [-1.8, +2.2] | +0.9 [-1.0, +3.0] |
| lr 1e-4, 300 steps | 48.5% / 52.1% | -2.1 [-4.2, +0.1] | +1.5 [-1.4, +4.1] |
| lr 2e-4, 300 steps | 43.1% / 53.8% | -7.5 [-10.0, -5.3] | +3.1 [+0.7, +5.5] |
| checkpoints selected on 32 validation seeds | 52.2% / 53.2% | +1.6 [-0.4, +3.6] | +2.6 [+0.7, +4.6] |

- **The point-mass arm and the untrained seed are unchanged:** 51.4%, 49.9%, 22.2%, 52.2% for the
point mass, and 50.6% for the seed.
- **Training through the dynamic model now helps on Jolt** rather than only hurting less:
- its validation reach rises steadily over training (0.497 -> 0.526 at step 210), where before it
fell to 0.41;
- the trained policy beats the untrained one outside the CI at lr 2e-4 and with checkpoint
selection.
- **At the default learning rate the change is within the noise:** +0.7 [-1.4, +2.9].

How the physics channels enter training:

- **Per unit mass.** Each channel is defined per unit mass, for example grade as g·∇z converted with
Expand Down
23 changes: 17 additions & 6 deletions inc/cvc/physics/nav_plant.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,26 @@ class nav_plant final : public cvc::nav::vehicle_plant {
// - mass; yaw inertia of the chassis box, m (hx^2 + hy^2) / 3;
// - the centre of gravity's distances to the front (steered) and rear axles, and its height
// as terrain_config_of has it;
// - Pacejka B and C from the tires' peak slip angle and sliding fraction: sin(C pi / 2) is
// tire_sliding, and the force peaks at tire_peak_slip_angle. vehicle_desc has one tire,
// so `front_stiffness` scales the front's B: a road vehicle's understeer comes from the
// front's load sensitivity and steering compliance, which the single track leaves out;
// - Pacejka C from the tires' sliding fraction (sin(C pi / 2) is tire_sliding) and B fitted to
// how Jolt's tires corner. Jolt's lateral friction ramps linearly to its peak at
// tire_peak_slip_angle, a cornering stiffness of 1 / alpha_peak per unit load, and a Pacejka
// tire peaking at the same angle starts 4.5x steeper (B C = C tan(pi / 2C) / alpha_peak).
// So B C = k / alpha_peak with k = kJoltTireStiffness (1.12), fitted to the one-tick
// residual of Jolt runs of the Humvee and the RoboRacer (libcvc #749's fit_dyn_bike;
// held-out twist residual about 11x lower than with the 4.5x tire). `front_stiffness`
// scales the front's B; Jolt's tires have no load sensitivity or steering compliance, and
// the fit puts front and rear alike (1);
// - the drive split from which axles are driven, the brake split from the axles' brake
// torques; drag 1/2 rho C_d A and the rolling resistance;
// - the steering's rate; `drive_lag` for the force (nav_plant_options::latency's default).
// - the steering's rate; `drive_lag` for the force, < 0 for the vehicle's own: a direct drive
// (one gear, no torque converter) answers within the tick (0), a geared drive through a
// converter takes 0.1 s (nav_plant_options::latency's default);
// - for a direct drive, no drag or rolling resistance: the driver model's integral trim tracks
// the commanded acceleration and makes up for them, so the commanded acceleration is the
// achieved one (the RoboRacer's fit: lag 0, resistance x0; the Humvee's keeps both).
static constexpr double kJoltTireStiffness = 1.12;
static cvc::nav::dyn_bike_si dyn_bike_si_of(const vehicle_desc &d, double mu = 0.9,
double front_stiffness = 0.85, double drive_lag = 0.1,
double front_stiffness = 1.0, double drive_lag = -1.0,
double gravity = 9.80665);

// The same vehicle in a normalized frame where its wheelbase is `wheelbase`: S =
Expand Down
15 changes: 10 additions & 5 deletions src/cvc/physics/nav_plant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,19 +314,24 @@ cvc::nav::dyn_bike_si nav_plant::dyn_bike_si_of(const vehicle_desc &d, double mu
si.cg_to_rear = std::max(1e-3, d.center_of_mass.x - rear);
si.cg_height = std::max(0.0, -z + travel + radius + d.center_of_mass.z);
si.mu = mu;
// Pacejka: sin(C pi / 2) = tire_sliding, C in (1, 2); the peak at B alpha = tan(pi / (2 C)).
// Pacejka: sin(C pi / 2) = tire_sliding, C in (1, 2). B makes the cornering stiffness per unit
// load, B C, kJoltTireStiffness times Jolt's: its lateral friction ramps linearly to 1 at
// tire_peak_slip_angle (jolt_backend.cpp).
const double kPi = 3.14159265358979323846;
const double sliding = std::min(std::max(d.tire_sliding, 0.05), 0.999);
si.tire_C = 2.0 - 2.0 * std::asin(sliding) / kPi;
const double B = std::tan(kPi / (2.0 * si.tire_C)) / std::max(d.tire_peak_slip_angle, 1e-3);
const double B = kJoltTireStiffness / (si.tire_C * std::max(d.tire_peak_slip_angle, 1e-3));
si.tire_B_front = B * front_stiffness;
si.tire_B_rear = B;
si.front_drive = front_driven && rear_driven ? 0.5 : (front_driven ? 1.0 : 0.0);
si.front_brake = brake_front + brake_rear > 0.0 ? brake_front / (brake_front + brake_rear) : 0.5;
si.drag = 0.5 * d.air_density * d.drag_coefficient * d.frontal_area;
si.rolling_resistance = d.rolling_resistance;
// A direct drive: one gear, no torque converter. The driver model's trim makes its commanded
// acceleration the achieved one within the tick, drag and rolling resistance included.
const bool direct = d.gear_ratios.size() <= 1 && !(d.converter_k > 0.0);
si.drag = direct ? 0.0 : 0.5 * d.air_density * d.drag_coefficient * d.frontal_area;
si.rolling_resistance = direct ? 0.0 : d.rolling_resistance;
si.steer_rate = d.max_steer_rate;
si.drive_lag = drive_lag;
si.drive_lag = drive_lag >= 0.0 ? drive_lag : (direct ? 0.0 : 0.1);
si.gravity = gravity;
return si;
}
Expand Down
44 changes: 22 additions & 22 deletions src/cvc/tests/data/dyn_bike_golden.json
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
{
"format": "cvc-dyn-bike-golden-1",
"order": ["x", "y", "psi", "vx", "vy", "r", "delta", "a"],
"params": {"m": 2676, "Iz": 6283.69384765625, "lf": 1.5369999408721924, "lr": 1.7630000114440918, "h": 0.74000000953674316, "g": 9.8066501617431641, "mu": 0.89999997615814209, "B_f": 36.121696472167969, "B_r": 42.496116638183594, "C": 1.2021652460098267, "front_drive": 0.5, "front_brake": 0.70588237047195435, "drag": 1.5006250143051147, "c_rr": 0.014999999664723873, "tau_steer": 0.05000000074505806, "steer_rate": 0.34999999403953552, "tau_drive": 0.10000000149011612, "v_blend": 1, "v_width": 0.30000001192092896, "eps": 0.5, "fz_min": 0.05000000074505806, "iters": 3},
"params": {"m": 2676, "Iz": 6283.69384765625, "lf": 1.5369999408721924, "lr": 1.7630000114440918, "h": 0.74000000953674316, "g": 9.8066501617431641, "mu": 0.89999997615814209, "B_f": 10.708646774291992, "B_r": 10.708646774291992, "C": 1.2021652460098267, "front_drive": 0.5, "front_brake": 0.70588237047195435, "drag": 1.5006250143051147, "c_rr": 0.014999999664723873, "tau_steer": 0.05000000074505806, "steer_rate": 0.34999999403953552, "tau_drive": 0.10000000149011612, "v_blend": 1, "v_width": 0.30000001192092896, "eps": 0.5, "fz_min": 0.05000000074505806, "iters": 3},
"g_out": [0.69999999999999996, -0.29999999999999999, 1.1000000000000001, 0.40000000000000002, -0.90000000000000002, 0.25, 0.59999999999999998, -0.14999999999999999],
"cases": [
{"state": [0, 0, 0, 12, 0.10000000000000001, 0.050000000000000003, 0.02, 0.5], "delta_cmd": 0.050000000000000003, "a_cmd": 1, "grade": 0,
"limits": {"L": 3.2999999523162842, "vmax": 40, "hdt": 0.059999998658895493, "allow_reverse": 0},
"next": [0.72161232476023529, 0.0030915364619978677, 0.006158445274966143, 12.026961596240618, 0.15841392030776888, 0.10264075687696875, 0.035978411112368727, 0.72559417581953878],
"g_state": [0.69999999999999996, -0.29999999999999999, 0.881352227048531, 0.43908416096250991, -0.30332956174135622, 0.11778155321430109, -1.2584661505628023, -0.067429376024266241],
"g_delta_cmd": -0.52467945254116677, "g_a_cmd": -0.05543495498191852},
"next": [0.72170075978362558, 0.0014655898551522932, 0.0048612338851567365, 12.028322883477824, 0.10879309514548365, 0.08102056656356306, 0.035978411112368727, 0.72559417581953878],
"g_state": [0.69999999999999996, -0.29999999999999999, 0.88246385916630588, 0.44344941191751436, -0.58216522580427366, 0.44787693050917154, -0.31518187358286542, -0.067550279387271248],
"g_delta_cmd": -0.13140556288176811, "g_a_cmd": -0.055534351904751283},
{"state": [1, -2, 0.29999999999999999, 15, -1.2, 0.59999999999999998, 0.25, 0], "delta_cmd": 0.29999999999999999, "a_cmd": 0, "grade": 0.02,
"limits": {"L": 3.2999999523162842, "vmax": 40, "hdt": 0.059999998658895493, "allow_reverse": 0},
"next": [1.8862789801906317, -1.8347158945971369, 0.33547355793444861, 14.854786313864151, -1.2194566888495753, 0.59122597878907412, 0.26954533644854789, 0],
"g_state": [0.69999999999999996, -0.29999999999999999, 0.71841743216080634, 0.46493637589482706, -0.92038287845088973, 1.1426390353048035, 0.4563570614107727, -0.077760090566516074],
"g_delta_cmd": 0.04704770737140862, "g_a_cmd": -0.063928029207825282},
"next": [1.8867829073011033, -1.8363951317071947, 0.3362258629537182, 14.851775313286971, -1.2377173130869106, 0.60376439605715648, 0.26954533644854789, 0],
"g_state": [0.69999999999999996, -0.29999999999999999, 0.71944172000470541, 0.4699842462121625, -0.88320019268164862, 1.0507783479334352, 0.44490310874960692, -0.076361021377209612],
"g_delta_cmd": 0.045866872760495392, "g_a_cmd": -0.062777828181229955},
{"state": [-3, 4, -1.1000000000000001, 14, 0.80000000000000004, 0.40000000000000002, 0.14999999999999999, -4], "delta_cmd": 0.14999999999999999, "a_cmd": -6, "grade": -0.029999999999999999,
"limits": {"L": 3.2999999523162842, "vmax": 40, "hdt": 0.059999998658895493, "allow_reverse": 0},
"next": [-2.6139870110986414, 3.2746823456478453, -1.070979653140653, 13.693204346336563, 0.7055992128641444, 0.48367245846670809, 0.14999999999999999, -4.9023767032781551],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.4919184613761005, 0.44997106717466218, -0.51421353207102205, 0.22798404545675632, 0.037739398844601396, -0.076678056992082619],
"g_delta_cmd": 0.087559812311952701, "g_a_cmd": -0.063038469107698336},
"next": [-2.6188025127373895, 3.2714360882174414, -1.0723766257472249, 13.702551861063213, 0.58732269798076042, 0.4603895811700956, 0.14999999999999999, -4.9023767032781551],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.495635492069008, 0.45176658307929768, -0.64916167547811943, 0.61889519194170228, -0.0099817306380819981, -0.073112649574912106],
"g_delta_cmd": -0.023158780690115677, "g_a_cmd": -0.060107280784200925},
{"state": [2, 2, 2.5, 10, 2.5, 1.1000000000000001, 0.40000000000000002, 1], "delta_cmd": 0.45000000000000001, "a_cmd": 2, "grade": 0,
"limits": {"L": 3.2999999523162842, "vmax": 40, "hdt": 0.059999998658895493, "allow_reverse": 0},
"next": [1.4901741447892105, 2.3297160167472559, 2.5727797258014662, 10.119081136118659, 2.0853344678711072, 1.2129954571369974, 0.41954533644854791, 1.4511883516390776],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.0221465448401579, 0.3680655773155822, -0.4105335944971768, -0.071966286436913274, 0.0064365975211407855, -0.068908097463021359],
"g_delta_cmd": 0.00066357504298502484, "g_a_cmd": -0.056650639617034933},
"next": [1.4909879252982987, 2.3339382319443089, 2.5710190927434646, 10.145752585238649, 1.9848686819797339, 1.1836515721810814, 0.41954533644854791, 1.4511883516390776],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.0189468600494942, 0.36877586997421102, -0.5469687755509326, 0.34522820509560315, -0.2583148462334196, -0.07068503726719233],
"g_delta_cmd": -0.026630729143777142, "g_a_cmd": -0.058111495164835755},
{"state": [0.5, 0.5, 0.20000000000000001, -2, 0.050000000000000003, -0.10000000000000001, 0.20000000000000001, -0.5], "delta_cmd": 0.25, "a_cmd": -0.80000000000000004, "grade": 0,
"limits": {"L": 3.2999999523162842, "vmax": 40, "hdt": 0.059999998658895493, "allow_reverse": 1},
"next": [0.38234157507615946, 0.47820894213237564, 0.19215389327768181, -1.9942410241949737, -0.21255053807775098, -0.13076844829487239, 0.2195453364485479, -0.63535650549172329],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.1505512679844894, 0.37186240076506216, -0.12784613682710022, 0.017428116952459605, 1.2345138787580188, -0.07080295209586783],
"g_delta_cmd": 0.12727106168621422, "g_a_cmd": -0.058208435157532337},
"next": [0.38163390582407375, 0.4801677888170704, 0.19227964555052293, -1.9995770449242993, -0.17430505462442358, -0.12867257703400767, 0.2195453364485479, -0.63535650549172329],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.1493923760808287, 0.39264945863371031, -0.21968909271960391, 0.10685582785702351, 1.1849225895596516, -0.070144184766086387],
"g_delta_cmd": 0.12215849378781682, "g_a_cmd": -0.057666850177465923},
{"state": [0, 0, 0, 0.59999999999999998, 0.02, 0.050000000000000003, 0.29999999999999999, 0.29999999999999999], "delta_cmd": 0.34999999999999998, "a_cmd": 0.5, "grade": 0,
"limits": {"L": 3.2999999523162842, "vmax": 40, "hdt": 0.059999998658895493, "allow_reverse": 0},
"next": [0.035991441415742745, 8.9957242432581056e-05, 0.003613523021362957, 0.5998588717055453, 0.10550903842574866, 0.060225385035524874, 0.31954533644854788, 0.39023767032781553],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.0891395975055744, 0.29781652332996111, 0.050398345344285118, 0.081666464100695529, 0.26493307713883441, -0.072170287614980372],
"g_delta_cmd": 0.027313029511808661, "g_a_cmd": -0.059332547338548272},
"next": [0.036071137283442252, -1.6430282470171074e-05, 0.0036051455458450143, 0.60118074078138972, 0.10349000305763768, 0.060085760440438306, 0.31954533644854788, 0.39023767032781553],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.0891901600126965, 0.30814654633137256, 0.022983162019829553, 0.083479081063051172, 0.27490937611823435, -0.071961486030844291],
"g_delta_cmd": 0.028341526788878316, "g_a_cmd": -0.059160887639182654},
{"state": [0, 0, 0, 0, 0, 0, 0, 0], "delta_cmd": 0.10000000000000001, "a_cmd": 2, "grade": 0,
"limits": {"L": 3.2999999523162842, "vmax": 40, "hdt": 0.059999998658895493, "allow_reverse": 0},
"next": [0.0032481983257981982, 5.3123536200254369e-08, 2.062374045329597e-05, 0.054136639980098399, 0.0006057631500759013, 0.00034372901523787501, 0.020946006673345115, 0.90237670327815533],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.0990255033157852, 0.42620268045264031, 0.0018837905264509067, 0.0068176134156984324, 0.57682703919426437, -0.068034399515581628],
"g_delta_cmd": 0.0020775379242710104, "g_a_cmd": -0.055932356144165696}
"next": [0.0032481985805457771, 1.4550464025321322e-08, 2.063503148721843e-05, 0.054136644212622924, 0.00060545142240215992, 0.00034391719914078894, 0.020946006673345115, 0.90237670327815533],
"g_state": [0.69999999999999996, -0.29999999999999999, 1.0990255302405116, 0.42627209229010737, -0.0055533005664103159, 0.0088728813432996916, 0.57684392487435543, -0.068034179542302314],
"g_delta_cmd": 0.0020775987408388598, "g_a_cmd": -0.055932175300006146}
]
}
Loading
Loading