Ford: derive C0 from selected desired curvature

This commit is contained in:
Isaac Barham
2026-09-13 13:08:46 -04:00
parent 08b3a14ad4
commit 20485134e1
14 changed files with 1428 additions and 55 deletions
+99
View File
@@ -0,0 +1,99 @@
# Desired-curvature C0 on continuous PI
This local candidate changes C0's reference from the live model path's lateral
position at 7 m to a circular arc of the selected, upstream-limited desired
curvature. It is based on v7 (`08b3a14ad`), with the same P=0.50/I=0.25 controller.
No new gain, state, release condition, reference delay or output limit is added.
The candidate is on `codex/ford-curvature-c0-trial`; this evaluation does not
publish it over the v7 onroad branch.
For selected curvature k and the existing 7 m reference distance:
```text
arc C0 = (1 cos(7 × k)) / k, or 0 when k = 0
≈ 24.5 × k for small curvature
C0 target = clip(arc C0 + 7 × clipped-away base C1, 5.11, +5.11)
```
The implementation uses the equivalent squared-sinc expression to avoid
subtracting nearly equal floating-point numbers near zero. C0 keeps its 4 m/s
output slew and Float32/CAN quantization. C1 keeps the existing mapping and PI
law, ±0.5 rad bound and 0.5 rad/s slew. C2 and C3 stay zero.
This is a geometric reference choice, not a model of the PSCM. The arc starts
at zero lateral position and heading. Independent live model-path position and
heading are omitted, while selected curvature can still include the model's
centering decision. Valid live model geometry remains a health gate. Short valid
paths do not shorten the synthetic 7 m arc. Both C0 and C1 use the selected
request, including the maneuver source when selected by controlsd.
## What the recorded routes show
All fourteen previous routes were replayed with v7 and this C0 replacement:
Lightning 112117, a0, a2, a5, a9, b8, b9, ca and Raptor 02. On all 1,578,250
control cycles, C1, P, I, activation, feedforward, overflow and feedback/PSCM
gates match exactly. The v7 baseline also reproduces its archived commands
exactly. C0 matches the earlier isolated geometry experiment, but C1 no longer
has the release rules that coupled it to C0 in that experiment.
Duration-weighted clean samples, grouped by requested steering-wheel angle:
| Absolute requested wheel angle | v7 mean absolute C0 | Curvature C0 | Reduction |
| --- | ---: | ---: | ---: |
| Under 5° | 0.0136 m | 0.0072 m | 46.7% |
| 530° | 0.0965 m | 0.0608 m | 37.0% |
| 3090° | 0.5064 m | 0.2916 m | 42.4% |
| At least 90° | 2.6314 m | 1.5282 m | 41.9% |
The clean cohort is 6,654.52 s with the existing quality/driver mask and margins,
speed at least 2 m/s and replay feedback enabled. Only 36.20 s have a requested
wheel angle of at least 90°. These are command magnitudes, not torque or tracking
scores, and do not classify driver interventions as controller failures.
At route 117, 128.272 s (right entry), C0 changes from 2.51 m to 1.35 m while
C1 remains 0.427 rad. At 142.670 s (request relaxing/reversing), C0 changes from
+0.18 m to 0.02 m while C1 remains 0.0425 rad. Both comparisons are replayed
on the same recorded vehicle motion. The new C0 follows the selected request
more directly, but it supplies less C0 during the entry as well as the exit.
## Validation and interpretation
- 694 Ford/controlsd, tracked PSCM lab, Sunnylink, params, sender and safety
tests pass; 9,145 subtests pass and 178 platform tests skip. Historical
untracked offline experiment tests are outside this deployment suite.
- The isolated two-controller replay performs 3,156,500 Float32/CAN checks.
- Production selection/adapter replay exactly reproduces the isolated candidate
on every route cycle, adding 1,578,250 Float32/CAN checks.
- A 20,000-cycle scalar geometry/PI stress with mirrored and unrelated model
paths adds 60,000 checks. Total: 4,794,750 CAN round trips.
- Tests cover zero/tiny curvature, signs, circular geometry, short/malformed
paths, selected maneuver requests, overflow, unwind, duplicate measurements,
model/driver/PSCM gates, caps/slew and toggle-off upstream Ford fallback.
- Ruff, production Ty and diff whitespace checks pass.
Software C1 parity does not guarantee identical physical unwind: changing C0
changes the PSCM's input and therefore the vehicle response and future feedback.
Smaller C0 is not established as better or worse tracking. No device build,
boot or drive of this candidate is claimed. Collecting the promising v7 drive's
logs before replacing it would preserve a useful comparison.
## Reproduction
Use the built cereal/opendbc environment and the same local route extracts:
```sh
export PYTHONPATH=.:opendbc_repo:.cache/ford_v6/test_deps
export PYTHONDONTWRITEBYTECODE=1
export PARAMS_ROOT=/tmp/ford-c0-params
export LOG_ROOT=/tmp/ford-c0-logs
python -m tools.ford_pscm_lab.curvature_c0_v7_replay .cache/ford_route117 --output .cache/ford_curvature_c0_v7/117
python -m tools.ford_pscm_lab.curvature_c0_validate --output .cache/ford_curvature_c0_v7/production --workers 4
python -m tools.ford_pscm_lab.curvature_c0_production_stress --cycles 20000 --output .cache/ford_curvature_c0_v7/production_stress.json
```
Repeat the first command for each label before validating all routes. Raptor
uses input `.cache/ford_raptor_route02` and output label `raptor02`. The first
replay loads isolated copies of pinned v7; the second tests this checkout's
actual selector and adapter. The validation JSON records source and extract
hashes, settings, example points and both sets of route reports. Older v7-only
production validation commands should run from the v7 commit.
+890
View File
@@ -0,0 +1,890 @@
{
"scope": "Curvature-derived C0 candidate on the continuous PI controller; fixed recorded motion, no physical tracking prediction.",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"branch": "codex/ford-curvature-c0-trial",
"deployment_at_evaluation": "local candidate; hiimisaac-dev remains v7",
"hypothesis": "model-action-curvature-c0-pi-v8",
"source_sha256": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc",
"kp": 0.5,
"ki": 0.25,
"station_m": 7.0,
"cycles": 1578250,
"route_count": 14,
"same_c1_and_integral_on_every_recorded_cycle": true,
"baseline_exactly_matches_archived_v7": true,
"c0_commands_match_prior_geometry_experiment": true,
"can_round_trips": 4794750,
"tests": {
"passed": 694,
"skipped": 178,
"subtests_passed": 9145,
"scope": "Same Ford controls, tracked PSCM lab, car status, Sunnylink, params, Ford car and safety suite as v7; historical untracked experiments excluded."
},
"ruff": "pass",
"ty_production": "pass",
"bins": [
{
"requested_wheel_angle_degrees": "0-5",
"seconds": 4863.242382185041,
"mean_abs_c0_m": [
0.013572257514406704,
0.007239040142159917
],
"reduction_pct": 46.662962042417725
},
{
"requested_wheel_angle_degrees": "5-30",
"seconds": 1576.952442509967,
"mean_abs_c0_m": [
0.09652124113032315,
0.06077627429873566
],
"reduction_pct": 37.03326481610879
},
{
"requested_wheel_angle_degrees": "30-90",
"seconds": 178.12595563801986,
"mean_abs_c0_m": [
0.5064290157685561,
0.2916269430470148
],
"reduction_pct": 42.4150406144399
},
{
"requested_wheel_angle_degrees": "90-inf",
"seconds": 36.19569558700505,
"mean_abs_c0_m": [
2.6314148997254545,
1.528200796263899
],
"reduction_pct": 41.92474944094366
}
],
"examples": [
{
"route": "116",
"time": 112.00019806100002,
"desired_angle": 59.83296203613281,
"actual_angle": 60.900001525878906,
"c0_left_positive": [
0.7599999999999998,
0.33999999999999986
],
"c1_both_left_positive": 0.13550000000000006,
"clean": true
},
{
"route": "117",
"time": 128.2721161039999,
"desired_angle": -226.72189331054688,
"actual_angle": -185.1999969482422,
"c0_left_positive": [
-2.5100000000000002,
-1.35
],
"c1_both_left_positive": -0.427,
"clean": true
},
{
"route": "116",
"time": 673.6933639450001,
"desired_angle": -152.83456420898438,
"actual_angle": -272.1000061035156,
"c0_left_positive": [
-2.21,
-0.9199999999999999
],
"c1_both_left_positive": -0.183,
"clean": true
},
{
"route": "117",
"time": 142.52000677500018,
"desired_angle": 6.6218461990356445,
"actual_angle": 40.599998474121094,
"c0_left_positive": [
0.2599999999999998,
0.040000000000000036
],
"c1_both_left_positive": -0.024499999999999966,
"clean": true
},
{
"route": "117",
"time": 142.66993146999994,
"desired_angle": -2.186957597732544,
"actual_angle": 28.5,
"c0_left_positive": [
0.17999999999999972,
-0.019999999999999574
],
"c1_both_left_positive": -0.04249999999999998,
"clean": true
}
],
"replay_reports": [
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 108971,
"controller_updates": 217942,
"active_cycles_per_controller": 91414,
"can_round_trips": 217942,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 919.4963447830002,
"clean_seconds": 678.5721400560004,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route112/route.npz": "2b08a2fb636f7d14556d7df4035eafc1d1b97932237955528562a16db2b31d3e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route112/model_paths.npz": "9837afe78aab4cad288cad98a595a5777fa8a66bb235986b1272a7f7c54e559a",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route112/metadata.json": "726d78a7e7aa45307dcfe27cb00775c20ecc9d54538eb7f26a0166fc216226ce",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route112/angles.npz": "9429813237b4988dd99a1461d1a9bd6f6d3056693a93a5dd1ec63567d19f2aa1",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 49614,
"controller_updates": 99228,
"active_cycles_per_controller": 27207,
"can_round_trips": 99228,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 273.884368861,
"clean_seconds": 195.66672679699968,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route113/route.npz": "774ca4a21b7113c2706d6130bc180c3216ea4833155300ab01e75b3486e36327",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route113/model_paths.npz": "93c41761eb85263f534f5371b905482cf7c948582eb1e9149966594be1d3768f",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route113/metadata.json": "1c0ca74dd48b90ab9d5444c5ca7f8aa9361700bbdf98bd5e853be50ad2895d7f",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route113/angles.npz": "2f280a1b5973901d878f1e36564d6e7ae6d817cac176149f2cb898ab7591d65b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 61027,
"controller_updates": 122054,
"active_cycles_per_controller": 48419,
"can_round_trips": 122054,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 487.557504183,
"clean_seconds": 290.3177928490001,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route114/route.npz": "83524f07d61104b84b004ddc46bb751ca7f61da67798aa47db56d307765f5b3e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route114/model_paths.npz": "14d14362d1a3e46398edd8e22b7cc4e36277a7596a73f546192d0e14c6642b07",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route114/metadata.json": "ec677b9275c1707e477ebd0ffa235d49b5ec63a1ee717b16040c3acdbcd3bdc0",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route114/angles.npz": "63aa2ff6a21e8613519bda476690e2eda6f42cb5dcf108606bc6072c9e29408d",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 40037,
"controller_updates": 80074,
"active_cycles_per_controller": 33976,
"can_round_trips": 80074,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 341.89711117800005,
"clean_seconds": 176.48340830000006,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route115/route.npz": "e0df32d9e80f1c6b7d56327b37cf07af60070ffc212b8d111e343306148bff23",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route115/model_paths.npz": "52f3ed9e188e4947618a57a3ed872fd1966a887fe1014999df741553b6c13bc0",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route115/metadata.json": "d3c73035bad8eb5059e07962fd274c19cb70c8952b6f1514835d312d08b87a16",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route115/angles.npz": "71415b0f49efbfeda495ae52bde4beb7e16f31a1f5570af515a365e756056d3b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 68793,
"controller_updates": 137586,
"active_cycles_per_controller": 52456,
"can_round_trips": 137586,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 527.966444073,
"clean_seconds": 218.019997628,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route116/route.npz": "13df1d49edd0c92bd4a4236c766ee76baa6dad477824b9de4b79927c98410394",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route116/model_paths.npz": "85d1112b5e89340e8a955b7fe4040b3135cf1c91a54ff756826c6119cc9cc01d",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route116/metadata.json": "d52fe63d013fa546524ad9522a26943d9372730d397d92d75dfd9d75c63ce766",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route116/angles.npz": "68e699e01c3ea291e6602ee53edf9cee27c0b461e16c57c3acc0ad12bf29b2aa",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 27301,
"controller_updates": 54602,
"active_cycles_per_controller": 15841,
"can_round_trips": 54602,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 159.4681704720001,
"clean_seconds": 74.58932843500065,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route117/route.npz": "65e4ea6c76dce73018b636479b3777116f1e58bb9813e6c1a7b79018b219140e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route117/model_paths.npz": "079f6003968287530e1ed6f2243c727e80c2ef3d16fb8a7d945d7eca744d54e7",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route117/metadata.json": "348396b3059c5f54d7938cd3acf2bb268a813b47ff47561da7a94cb75c3f3114",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route117/angles.npz": "0575b2d888a6848649b9fb1f7df7c72140f0abbceaa1c451b6eecd034729ef7b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 114582,
"controller_updates": 229164,
"active_cycles_per_controller": 105382,
"can_round_trips": 229164,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 1061.1945591,
"clean_seconds": 884.2159145080002,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea0/route.npz": "01ce8b0361c3154d07d5449525a650baa2cc261675095cb1f04ec9e37a9174d3",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea0/model_paths.npz": "5d0c69d10a6d13882b47d950ae64c4d98f7ca2b59eab27e4e9557eef3bc4917b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea0/metadata.json": "00496a11afbee475e9b5d66838a4d1663a3c0063cd562b4d1408cce6d71e586e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea0/angles.npz": "feb7b558d75292ab69ce8657540f3295bbc1b0c915d27f39d831bdece3fac534",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 71111,
"controller_updates": 142222,
"active_cycles_per_controller": 43430,
"can_round_trips": 142222,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 437.0644468180001,
"clean_seconds": 332.6767952400001,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea2/route.npz": "af4128d278be6be0042f72a02255ddbec96a8a5aa435b9852c8202e4a5cb39e5",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea2/model_paths.npz": "5a5a7c4fc30c7b3bc7f2413daa8b11fe86b69dd3249e8df2d35b0a0636a49717",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea2/metadata.json": "cfecd240216e409a1e2591c6c2e6f0bad002ecd2a50c8f6dbf22a8f9174708da",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea2/angles.npz": "2424b32f02f7e5079f8ae6ffdd30a811820d1d1bfa5b17636c5457eb67d64ecb",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 38961,
"controller_updates": 77922,
"active_cycles_per_controller": 35677,
"can_round_trips": 77922,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 359.186785903,
"clean_seconds": 249.91415584299997,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea5/route.npz": "815d1e248ff5c3e5e5cfc11dfbd0690d0d13ddafe1436890e239975a72dae9b8",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea5/model_paths.npz": "72e014b844322df6318f6af588c27175ce68d9a37bda62b70eafca5bdfe6dfe4",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea5/metadata.json": "c533b06d1b7c598330b36b117bdb3624221bc4193ded8354246d5c86f36b2628",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea5/angles.npz": "5466a674cae3f63eca8758f9f370a8c6cdee3dbb6bcf115f85e3b2e95cf5c1fb",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 286319,
"controller_updates": 572638,
"active_cycles_per_controller": 152043,
"can_round_trips": 572638,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 1530.3877326270015,
"clean_seconds": 1093.2748892979992,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea9/route.npz": "3fa8cabfb729dd42d689de38618d1d21c8965c9d60f914546f4d7cc58db8c975",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea9/model_paths.npz": "7f3646ad24aa52de5982c65600813de5bbc5f7664d92a2516bbba35da6e7b240",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea9/metadata.json": "403d35504bf596144845ac2060ce067d571f49a2955d16223af28a79a7a66e93",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea9/angles.npz": "cfcc9743aacfadc7be97e9ca130453c2ee6f7870edc3e98ccf9a873dac6d4897",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 160431,
"controller_updates": 320862,
"active_cycles_per_controller": 68217,
"can_round_trips": 320862,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 686.4382411050101,
"clean_seconds": 390.78219015704235,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb8/route.npz": "6f5dd369b70eaed4b95b28c8b25c9f2e9b830fa07a334881a185505481667c8b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb8/model_paths.npz": "939af6cf7e74251d8842581cc078d26d9fbfd22a0d7817cb0e368697d419b615",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb8/metadata.json": "73b439132d1de37ec187b544c04d2b05c80965065515a4b7dec29ba57ae37e7c",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb8/angles.npz": "6f4ea54d101595a48d301a0914c940d6ad932249b14d4eaa4fd7e43a11dc6f92",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 90774,
"controller_updates": 181548,
"active_cycles_per_controller": 86474,
"can_round_trips": 181548,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 870.449417766,
"clean_seconds": 604.9269013949997,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb9/route.npz": "b07c789d8155335f5d120d0262fced6e4d5803fe767b0ff49b6413dce4140b5c",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb9/model_paths.npz": "6b1f87897c050273fdc05af051307a049b6fc3a93072e7cda1721195ce7c3861",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb9/metadata.json": "9ce452220cab61b81883f32fc2fcaf5db6c78a674cb255a49cc77d5029580fee",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb9/angles.npz": "ce16212d9b13e41993d2c8df3246ddfe21324ad0cb213310e62eee1d685b197b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 327448,
"controller_updates": 654896,
"active_cycles_per_controller": 118756,
"can_round_trips": 654896,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 1194.5832199259985,
"clean_seconds": 894.0687052669919,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeca/route.npz": "ae9d46770eaf0dbbac6af86aebc926320eed0cf114eb43d5f78b0676e8e0dbf9",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeca/model_paths.npz": "bf17deb442383aaa79432566cd382df24a1bbbbd0521d0cafab956618f5bdd96",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeca/metadata.json": "a759d5cdf878df8b05d91db637b1935b6b4bdd87af96f0f256b67e7d809b3525",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeca/angles.npz": "1681bda6db7dac332abf627d97e7452acd7e0faef0c196b11e55e22291cad073",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
},
{
"scope": "Circular-arc C0 experiment against the pinned continuous PI v7 controller.\n\nBoth controllers run on identical recorded motion. Replace only the encoder's\nC0 target, retaining path validity, C1 overflow, continuous PI feedback and\noutput limits. No onroad selector or production module is modified.\n",
"baseline_revision": "08b3a14ad46260fda0f8d3a1c2cee2d272504153",
"baseline_source_sha256": "a899b8595e903d6fc4401392a8d123ef77725f6d6d7fa10392a7b3fe72e732b8",
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"proportional_gain": 0.5,
"integral_gain": 0.25,
"station_m": 7.0,
"cycles": 132881,
"controller_updates": 265762,
"active_cycles_per_controller": 64776,
"can_round_trips": 265762,
"same_activation_p_feedforward_overflow_and_feedback_gates": true,
"changed_c1_cycles": 0,
"max_abs_c1_change_rad": 0.0,
"changed_integral_cycles": 0,
"pscm_status_fresh_active_seconds": 651.3446633269996,
"clean_seconds": 571.0075301469988,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_raptor_route02/route.npz": "8a4cfe53994988d053b47d9caadf21ebd064fd20e598fa05ddc2da245bd0e980",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_raptor_route02/model_paths.npz": "d5dc6f72d91472b8f2fb1add1680cdaea1e122774ddd6d8519fd7673daf71bb5",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_raptor_route02/metadata.json": "9602e08efc2bd784133837c4156c075bb89bad3bedd6b309f15e884ed20e338a",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_raptor_route02/angles.npz": "82af2973ad40c1df392e11bdc5dd5895bfc6c170fc894735123af7704d95cd57",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_v7_replay.py": "fcd8a10db3d68024dbc836dce1b4c9a459912837b1a4234f61b2dd508c6d5e9f"
},
"limitations": [
"No new steering trace or physical tracking score: recorded motion stays fixed.",
"The baseline is v7 on every route, regardless of its recorded controller version.",
"C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.",
"Publication time proxies computation time; full SubMaster state is unavailable.",
"The synthetic arc keeps the original model-health gates for a controlled comparison."
]
}
],
"production_parity": [
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "112",
"cycles": 108971,
"can_round_trips": 108971,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route112/route.npz": "2b08a2fb636f7d14556d7df4035eafc1d1b97932237955528562a16db2b31d3e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route112/model_paths.npz": "9837afe78aab4cad288cad98a595a5777fa8a66bb235986b1272a7f7c54e559a",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route112/metadata.json": "726d78a7e7aa45307dcfe27cb00775c20ecc9d54538eb7f26a0166fc216226ce",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/112/report.json": "1be0b52e00e8f67043ce35e19cc6612f788d6e3853b40d1bc2dd372b2ff4e70d",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/112/commands.npz": "3951aa6160295ee6c3835517b39c7c835074b93b1bda5fb013d4ef56f88b8f1e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "113",
"cycles": 49614,
"can_round_trips": 49614,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route113/route.npz": "774ca4a21b7113c2706d6130bc180c3216ea4833155300ab01e75b3486e36327",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route113/model_paths.npz": "93c41761eb85263f534f5371b905482cf7c948582eb1e9149966594be1d3768f",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route113/metadata.json": "1c0ca74dd48b90ab9d5444c5ca7f8aa9361700bbdf98bd5e853be50ad2895d7f",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/113/report.json": "c6e98c0f2e6f43f1ede8d8104b77606a91fb3e95efa8348f7f089e01a4f6c823",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/113/commands.npz": "0a7aa93e8e3e64659022dac254b476e06fb95649c4b97e1de66e4808fae73d6f",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "114",
"cycles": 61027,
"can_round_trips": 61027,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route114/route.npz": "83524f07d61104b84b004ddc46bb751ca7f61da67798aa47db56d307765f5b3e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route114/model_paths.npz": "14d14362d1a3e46398edd8e22b7cc4e36277a7596a73f546192d0e14c6642b07",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route114/metadata.json": "ec677b9275c1707e477ebd0ffa235d49b5ec63a1ee717b16040c3acdbcd3bdc0",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/114/report.json": "b7f1d341f4f33d2c16dcfa2694b36cafe0bf76405d0e7b61bc4e469caef6b1dc",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/114/commands.npz": "3b79d094ecb89d3f3554c492d83826ec60662c236d6854891feba95fffcd7bc7",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "115",
"cycles": 40037,
"can_round_trips": 40037,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route115/route.npz": "e0df32d9e80f1c6b7d56327b37cf07af60070ffc212b8d111e343306148bff23",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route115/model_paths.npz": "52f3ed9e188e4947618a57a3ed872fd1966a887fe1014999df741553b6c13bc0",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route115/metadata.json": "d3c73035bad8eb5059e07962fd274c19cb70c8952b6f1514835d312d08b87a16",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/115/report.json": "e66d2be86136b0f6fdbcc2998673b2d1e8487f0543f99e4618e46a2f429538e4",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/115/commands.npz": "9a72eb6bab9116cc83210c92dfb12c5d63a2e4b3103ccdd3f5e70214f3f0e142",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "116",
"cycles": 68793,
"can_round_trips": 68793,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route116/route.npz": "13df1d49edd0c92bd4a4236c766ee76baa6dad477824b9de4b79927c98410394",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route116/model_paths.npz": "85d1112b5e89340e8a955b7fe4040b3135cf1c91a54ff756826c6119cc9cc01d",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route116/metadata.json": "d52fe63d013fa546524ad9522a26943d9372730d397d92d75dfd9d75c63ce766",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/116/report.json": "501b944c65f41f6c1e05fccdc6c38f3a80450db6c4696adc35bf85640a04df94",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/116/commands.npz": "2469c545d0ce62894d59e07799942ebf40bec583693391aaa1dd9e75c218a396",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "117",
"cycles": 27301,
"can_round_trips": 27301,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route117/route.npz": "65e4ea6c76dce73018b636479b3777116f1e58bb9813e6c1a7b79018b219140e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route117/model_paths.npz": "079f6003968287530e1ed6f2243c727e80c2ef3d16fb8a7d945d7eca744d54e7",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_route117/metadata.json": "348396b3059c5f54d7938cd3acf2bb268a813b47ff47561da7a94cb75c3f3114",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/117/report.json": "896b30e7ded613f1b05b693067fc2f19155016b6a33f26f881bbfff2b193019d",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/117/commands.npz": "6d88bfdb54fde00240954db9dbbf1f3e5059bfac9b6161c138f0069213ac35a8",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "a0",
"cycles": 114582,
"can_round_trips": 114582,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea0/route.npz": "01ce8b0361c3154d07d5449525a650baa2cc261675095cb1f04ec9e37a9174d3",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea0/model_paths.npz": "5d0c69d10a6d13882b47d950ae64c4d98f7ca2b59eab27e4e9557eef3bc4917b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea0/metadata.json": "00496a11afbee475e9b5d66838a4d1663a3c0063cd562b4d1408cce6d71e586e",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a0/report.json": "52cd87929fbdc721450c735bd02ac049260cafa439af7dcdcd405e977dc9ea22",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a0/commands.npz": "0f718892b0802cbf754ba33c9c9c6da8a3e915e88fa7a4e008b9b33d63a429e0",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "a2",
"cycles": 71111,
"can_round_trips": 71111,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea2/route.npz": "af4128d278be6be0042f72a02255ddbec96a8a5aa435b9852c8202e4a5cb39e5",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea2/model_paths.npz": "5a5a7c4fc30c7b3bc7f2413daa8b11fe86b69dd3249e8df2d35b0a0636a49717",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea2/metadata.json": "cfecd240216e409a1e2591c6c2e6f0bad002ecd2a50c8f6dbf22a8f9174708da",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a2/report.json": "fbaf59a1499df5dba0f790f76f211efefb3b68f327e9e49c763fb175f6f8e7a9",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a2/commands.npz": "159f76d2d511af7f423941cc9d168c7112a8aec7a34e05bbd043f65a2324b325",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "a5",
"cycles": 38961,
"can_round_trips": 38961,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea5/route.npz": "815d1e248ff5c3e5e5cfc11dfbd0690d0d13ddafe1436890e239975a72dae9b8",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea5/model_paths.npz": "72e014b844322df6318f6af588c27175ce68d9a37bda62b70eafca5bdfe6dfe4",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea5/metadata.json": "c533b06d1b7c598330b36b117bdb3624221bc4193ded8354246d5c86f36b2628",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a5/report.json": "ba9cedd6d9f2bbe2caa3ad0eeb298fa3334750ad60086422d7d48f3b690480f7",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a5/commands.npz": "8ac5c77a29824b8f621c3071309990d320a1a61cc968ad55f76408a530680913",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "a9",
"cycles": 286319,
"can_round_trips": 286319,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea9/route.npz": "3fa8cabfb729dd42d689de38618d1d21c8965c9d60f914546f4d7cc58db8c975",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea9/model_paths.npz": "7f3646ad24aa52de5982c65600813de5bbc5f7664d92a2516bbba35da6e7b240",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routea9/metadata.json": "403d35504bf596144845ac2060ce067d571f49a2955d16223af28a79a7a66e93",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a9/report.json": "677a2f66ecc5696fa44190c5e3ee27cd3dc45f1deda9ffe3a2f330b4e1d0d20d",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/a9/commands.npz": "e109e77124dc2296a00f65563c1b1ec3c960b414473a3cc7c6caaafbe7e65037",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "b8",
"cycles": 160431,
"can_round_trips": 160431,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb8/route.npz": "6f5dd369b70eaed4b95b28c8b25c9f2e9b830fa07a334881a185505481667c8b",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb8/model_paths.npz": "939af6cf7e74251d8842581cc078d26d9fbfd22a0d7817cb0e368697d419b615",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb8/metadata.json": "73b439132d1de37ec187b544c04d2b05c80965065515a4b7dec29ba57ae37e7c",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/b8/report.json": "f48796acdfad87a0caaaddac2a41945c97311e85fa57f9fc73a1730d264ddba4",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/b8/commands.npz": "554891332a496498ff64e08e4eba4210125fd5f2da8307231dd6d9377becfe0a",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "b9",
"cycles": 90774,
"can_round_trips": 90774,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb9/route.npz": "b07c789d8155335f5d120d0262fced6e4d5803fe767b0ff49b6413dce4140b5c",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb9/model_paths.npz": "6b1f87897c050273fdc05af051307a049b6fc3a93072e7cda1721195ce7c3861",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeb9/metadata.json": "9ce452220cab61b81883f32fc2fcaf5db6c78a674cb255a49cc77d5029580fee",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/b9/report.json": "3812983a690e5eafca93ea6af05620fe82e4e0cc8efc07e24505a63664444d66",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/b9/commands.npz": "2ffc685e29463a7b89d39bfb3583a37eb10a57d7201a24394550fcf9a89e3c4c",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "ca",
"cycles": 327448,
"can_round_trips": 327448,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeca/route.npz": "ae9d46770eaf0dbbac6af86aebc926320eed0cf114eb43d5f78b0676e8e0dbf9",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeca/model_paths.npz": "bf17deb442383aaa79432566cd382df24a1bbbbd0521d0cafab956618f5bdd96",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_routeca/metadata.json": "a759d5cdf878df8b05d91db637b1935b6b4bdd87af96f0f256b67e7d809b3525",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/ca/report.json": "753760c726ef1b85daf83b7108f758f9304dab408b49018f303616b99c96b81f",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/ca/commands.npz": "3fa932ac86e14440d325a6cc4677b47cd75c5c6019b929d3a5d078a62c5590df",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
{
"scope": "Verify production commands against the archived curvature-C0 offline candidate.\n\nFixed recorded motion verifies integration parity, not physical tracking.\n",
"route": "raptor02",
"cycles": 132881,
"can_round_trips": 132881,
"production_matches_archived_trial_exactly": true,
"opendbc_revision": "64aa61b9b3fd26e70a7caa915acab207ff3cd64a",
"hypothesis": "model-action-curvature-c0-pi-v8",
"kp": 0.5,
"ki": 0.25,
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_raptor_route02/route.npz": "8a4cfe53994988d053b47d9caadf21ebd064fd20e598fa05ddc2da245bd0e980",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_raptor_route02/model_paths.npz": "d5dc6f72d91472b8f2fb1add1680cdaea1e122774ddd6d8519fd7673daf71bb5",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_raptor_route02/metadata.json": "9602e08efc2bd784133837c4156c075bb89bad3bedd6b309f15e884ed20e338a",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/raptor02/report.json": "af6b8fa4e4bfe3d5543794cdde4fbe4fd7560addf02717c2b6144c3987effafa",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/.cache/ford_curvature_c0_v7/raptor02/commands.npz": "99ee65b866eda85e5c5d54f8896a7f952504826753eca31a01a29c7cf36c6482",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_validate.py": "c91ead82b0320725276141b1bc9b84b132cd6345a144ea826ba2b7f069870932",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
}
],
"production_stress": {
"cycles": 20000,
"kp": 0.5,
"ki": 0.25,
"controller_updates": 60000,
"can_round_trips": 60000,
"seed": 20260913,
"independent_c0_comparisons": 20000,
"checks": "Independent scalar PI/unwind-first/anti-windup arithmetic, mirror symmetry, exact C0 independence, limits, slew, resets, CAN.",
"source_sha256": {
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/curvature_c0_production_stress.py": "4bf031a793efe9ebc066e5b6db6897ca8e844d5e34c6faecd4701a4d8f04132a",
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "6f8c4a6f7fa54fa2e23f32f1e83ace83da5f0b6f45011941f1902a1dae778ffc"
}
},
"limitations": [
"C1 parity applies only to fixed recorded inputs; changing C0 changes physical response and subsequent feedback.",
"Curvature arc omits independent live model position/heading; seven meters remains an engineering reference choice.",
"Smaller C0 is not proof of better or worse tracking; no candidate hardware drive has been performed."
]
}
+16 -8
View File
@@ -1,14 +1,19 @@
# Ford selected-action drive-test branch
The current v7 experiment uses [continuous C1 PI feedback](ford_c1_minimal_pi.md)
with **P=0.50 and I=0.25**, replacing the earlier conditional-release rules.
Only C0, C1 and accumulated error carry control history. The restored model
mapping and [base C1 overflow allocation to C0](ford_c1_overflow.md) remain.
This local v8 candidate uses [curvature-derived C0](ford_curvature_c0_v8.md)
and [continuous C1 PI feedback](ford_c1_minimal_pi.md)
with **P=0.50 and I=0.25**.
Only C0, C1 and accumulated error carry control history. C0 is now a 7 m circular arc from selected desired curvature.
[Base C1 overflow allocation to C0](ford_c1_overflow.md) remains.
It is selectable on **any Ford CAN FD vehicle**
through the existing persistent, default-off Sunnylink
toggle. Offline checks establish software behavior; physical tracking,
turn-exit behavior and closed-loop stability remain unvalidated.
The v8 implementation is on local branch `codex/ford-curvature-c0-trial`.
This evaluation does not push it to `hiimisaac-dev`; the deployed v7 commit is
`08b3a14ad`. The selection instructions below apply once a candidate is installed.
## Select and restore
1. Install branch `hiimisaac-dev` from `sunnypilot/sunnypilot` using the device's
@@ -21,7 +26,7 @@ turn-exit behavior and closed-loop stability remain unvalidated.
The startup event `Ford path controller selected` should report
`FordModelActionController`. Periodic `Ford C2-free path tracking` events
identify **`hypothesis=model-action-c1-pi-v7`**. They report desired and measured
identify **`hypothesis=model-action-curvature-c0-pi-v8`**. They report desired and measured
curvature, base heading, proportional and accumulated correction, applied heading,
feedback timing and driver/PSCM gating. `proportional_gain=0.5` and
`integral_gain=0.25` identify the trial. `offset_overflow` reports the extra C0
@@ -51,7 +56,9 @@ combined feedforward/P/I amplitude and slew envelope. There is no C0 confirmatio
threshold or remembered turn direction. Zero error removes P and holds I; it
does not trigger a release. Final command limits still apply.
C0 starts with the original 7 m model-path mapping. When the raw base heading
C0 starts with the 7 m circular arc of selected desired curvature. It does not
add independent live model-path position or heading. Valid model geometry is
still required as a health gate. When the raw base heading
exceeds ±0.5 rad, C0 additionally receives 7 m times the clipped-away heading.
Accumulated C1 feedback does not spill into C0. The extra target returns to zero
as the base heading falls below the cap; applied C0 still follows its 4 m/s slew.
@@ -61,8 +68,9 @@ place. An explicit selection flag distinguishes upstream mode from an invalid
experimental command; invalid experimental input cannot switch to upstream.
The opendbc sender restores upstream behavior when that flag is false.
See [continuous PI trial and validation](ford_c1_minimal_pi.md) and
`ford_c1_minimal_pi_validation.json` for current evidence and reproduction commands.
See [curvature-C0 trial and validation](ford_curvature_c0_v8.md) and
`ford_curvature_c0_v8_validation.json` for this candidate.
[Continuous PI](ford_c1_minimal_pi.md) and its validation JSON record v7.
[Proportional feedback](ford_c1_pi.md) and its validation JSON record v6.
[Completed-unwind release](ford_unwind_catchup.md) and
`ford_unwind_catchup_validation.json` record v5. [Changed-request release](ford_c1_request_release.md) and its
@@ -1,6 +1,6 @@
"""Opt-in Ford C2-free model mapping with measured-curvature PI feedback.
C0 samples the model at 7 m, including clipped base-heading overflow. C1
C0 samples a desired-curvature arc at 7 m, including base-heading overflow. C1
combines the selected curvature's heading with proportional and integrated
tracking error. Reference distance and gains are explicit trial choices.
"""
@@ -34,10 +34,10 @@ def _finite(*values):
def encode_model_action(model, desired_curvature, speed):
"""Encode y(7) and max(7, v*1s)*selected limited curvature.
"""Encode a 7 m circular-arc offset and max(7, v*1s)*selected curvature.
Preserve the reviewed core's endpoint hold when the path ends before 7 m.
This samples the available geometry; it does not extrapolate an unseen path.
The arc starts at zero lateral position and heading. Original model geometry
remains a health gate; selected curvature supplies both path commands.
"""
if not _finite(desired_curvature, speed) or not .3 <= speed <= 55 or abs(desired_curvature) > 1:
return FordPath()
@@ -47,8 +47,10 @@ def encode_model_action(model, desired_curvature, speed):
return FordPath()
if path is None or not all(_finite(*values) for values in path):
return FordPath()
station, _, lateral, _ = path
c0 = float(np.interp(min(OFFSET_STATION_M, station[-1]), station, lateral))
# (1-cos(S*k))/k, using sinc to avoid cancellation near zero curvature.
half_heading = .5*OFFSET_STATION_M*desired_curvature
sinc = math.sin(half_heading)/half_heading if half_heading else 1.
c0 = .5*desired_curvature*OFFSET_STATION_M**2*sinc**2
c1 = max(OFFSET_STATION_M, speed*HEADING_TIME_S)*desired_curvature
return FordPath(True, c0, c1, 0., 0.) if _finite(c0, c1) else FordPath()
@@ -127,7 +129,7 @@ class FordModelActionController:
"""
def __init__(self, proportional_gain=C1_PROPORTIONAL_GAIN, integral_gain=C1_INTEGRAL_GAIN):
self.core = ModelActionController(proportional_gain=proportional_gain, integral_gain=integral_gain)
self.hypothesis = 'model-action-c1-pi-v7'
self.hypothesis = 'model-action-curvature-c0-pi-v8'
self.reset()
def reset(self, status='inactive'):
@@ -53,7 +53,7 @@ class TestFordControlsLogging(unittest.TestCase):
controls = SimpleNamespace(ford_path_controller=controller, desired_curvature=.03, curvature=.015,
sm=SimpleNamespace(logMonoTime={'modelV2': 123456789, 'carState': 123450000}))
record = self.emit_controls_event('Ford C2-free path tracking', controls)
self.assertEqual(record['hypothesis'], 'model-action-c1-pi-v7')
self.assertEqual(record['hypothesis'], 'model-action-curvature-c0-pi-v8')
self.assertIs(record['calibration_approved'], False)
self.assertEqual(record['command'][2:], [0., 0.])
self.assertEqual(record['status'], controller.diagnostics['status'])
@@ -25,17 +25,18 @@ def straight(offset=0.):
return make_model(x, np.full_like(x, offset), np.zeros_like(x))
def test_selected_action_controls_heading_even_when_model_previews_another_turn():
def test_selected_action_controls_both_fields_even_when_model_previews_another_turn():
model = circle(.02)
assert encode_model_action(model, 0., 20.).path_angle == 0.
assert encode_model_action(model, -.004, 20.).path_angle == pytest.approx(-.08)
assert encode_model_action(model, 0., 20.).path_offset > 0.
assert encode_model_action(model, 0., 20.).path_offset == 0.
assert encode_model_action(model, -.004, 20.).path_offset < 0.
def test_centering_information_is_independent_of_action_and_not_scaled_with_speed():
def test_arc_offset_uses_selected_curvature_and_is_not_scaled_with_speed():
for speed in (2., 7., 20., 35.):
target = encode_model_action(straight(.4), 0., speed)
assert target == FordPath(True, .4, 0., 0., 0.)
assert target == FordPath(True, 0., 0., 0., 0.)
for sign in (-1, 1):
target = encode_model_action(circle(sign*.01), sign*.01, 20.)
assert target.path_offset == pytest.approx(sign*(1-math.cos(.07))/.01, abs=1e-6)
@@ -74,7 +75,7 @@ def test_current_model_replacement_leaves_only_independent_actuator_slew():
controller = ModelActionController()
for _ in range(150):
controller.update(straight(1.), .04, current_curvature=.04, speed=20., dt=.01)
# C0 starts at 1 + 7*(.8-.5) = 3.1 m, including the clipped heading.
# C0 starts near .974 + 7*(.8-.5) = 3.074 m, including heading overflow.
for _ in range(78):
out = controller.update(straight(), 0., current_curvature=0., speed=20., dt=.01)
assert out.path_offset == pytest.approx(0.)
@@ -126,9 +127,11 @@ def test_selected_core_reversal_through_float32_and_wire_keeps_sign_and_zero_c2(
assert decoded['LatCtlCurv_No_Actl'] == decoded['LatCtlCrv_NoRate2_Actl'] == 0.
def test_short_path_holds_available_endpoint_without_extrapolation():
def test_short_valid_path_does_not_shorten_the_selected_curvature_arc():
model = make_model([0., 1.], [0., .1], [0., 0.])
assert encode_model_action(model, .01, 20.) == FordPath(True, .1, .2, 0., 0.)
target = encode_model_action(model, .01, 20.)
assert target == encode_model_action(straight(), .01, 20.)
assert target.path_offset == pytest.approx((1-math.cos(.07))/.01)
def test_overflowing_arc_resets_instead_of_publishing_invalid_geometry():
@@ -177,18 +180,31 @@ def test_domain_and_elapsed_time_boundaries(field, value, valid):
assert ModelActionController().update(straight(.4), current_curvature=kwargs['desired_curvature'], **kwargs).valid == valid
def test_arc_station_not_forward_x_or_model_heading_determines_offset():
def test_unrelated_live_model_position_and_heading_do_not_change_selected_arc():
x = np.array([0., 6., 12.])
y = .4+x*.75
target = encode_model_action(make_model(x, y, [2., -2., 1.]), -.01, 20.)
# Arc length is 1.25*x on this line, so y(arc=7)=.4+.75*(7/1.25).
assert target.path_offset == pytest.approx(4.6)
assert target.path_offset == pytest.approx(-(1-math.cos(.07))/.01)
assert target.path_angle == pytest.approx(-.2)
def test_duplicate_stations_keep_valid_geometry_and_first_cycle_slew():
model = make_model([0., 0., 10.], [.4, .4, .4], [0., 0., 0.])
assert encode_model_action(model, .01, 20.) == FordPath(True, .4, .2, 0., 0.)
assert encode_model_action(model, .01, 20.) == encode_model_action(straight(), .01, 20.)
out = ModelActionController().update(model, .01, current_curvature=.01, speed=20., dt=.002)
assert out.path_offset == pytest.approx(.01)
assert out.path_angle == pytest.approx(.001)
@pytest.mark.parametrize('curvature', [-1., -.2, -.1, -.01, -.001, .001, .01, .1, .2, 1.])
def test_desired_curvature_arc_matches_circle_geometry(curvature):
target = encode_model_action(straight(.4), curvature, 20.)
assert target.valid
assert target.path_offset == pytest.approx((1-math.cos(7.*curvature))/curvature, abs=1e-12)
@pytest.mark.parametrize('curvature', [-1e-12, -1e-100, 0., 1e-100, 1e-12])
def test_near_zero_arc_is_finite_continuous_and_keeps_direction(curvature):
target = encode_model_action(straight(.4), curvature, 20.)
assert target.valid and math.isfinite(target.path_offset)
assert target.path_offset == pytest.approx(24.5*curvature, rel=1e-12, abs=0.)
@@ -83,7 +83,7 @@ def test_repeated_measurements_do_not_freeze_slew_or_cache_invalid_model_geometr
controller = FordModelActionController()
for i in range(10):
result = update(controller, 1.+i*.01, measurement_time=1., model_time=1., reference_time=1.)
assert result.path_offset == pytest.approx(.4)
assert result.path_offset == pytest.approx(.24)
assert result.path_angle == pytest.approx(.05)
broken = straight(.4)
broken.position.y[5] = math.nan
@@ -106,18 +106,18 @@ def test_reference_source_can_change_to_an_older_but_fresh_publication():
assert update(controller, 1.01, reference_time=.98).valid
def test_release_keeps_current_geometry_and_may_grow_c0_while_c1_decreases():
def test_relaxing_selected_request_releases_both_fields_despite_growing_model_path():
for sign in (-1., 1.):
controller = FordModelActionController()
for i in range(100):
before = update(controller, 1.+i*.01, model=circle(sign*.01), desired_curvature=sign*.005)
for i in range(100):
after = update(controller, 2.+i*.01, model=circle(sign*.02), desired_curvature=sign*.004)
assert abs(after.path_offset) > abs(before.path_offset)
assert abs(after.path_offset) < abs(before.path_offset)
assert abs(after.path_angle) < abs(before.path_angle)
for i in range(100):
released = update(controller, 3.+i*.01, model=circle(sign*.02), desired_curvature=0.)
assert released.path_offset == after.path_offset
assert released.path_offset == pytest.approx(0.)
assert released.path_angle == pytest.approx(0.)
@@ -183,7 +183,7 @@ def test_actual_controlsd_selection_limiting_publication_and_downstream_can(pipe
assert controller.core.proportional == pytest.approx(.5*20.*expected_curvature)
assert controller.core.correction == 0. # First measurement has no elapsed feedback time.
assert controls.ford_path.path_angle == pytest.approx((-1 if maneuver else 1)*.0035)
assert controls.ford_path.path_offset == pytest.approx(.04)
assert controls.ford_path.path_offset == pytest.approx(0.) # Limited curvature arc is below one C0 step.
assert cc.latActive and cc.actuators.curvature == 0.
assert controller.diagnostics['reference_age'] == pytest.approx(.01 if maneuver else .02)
@@ -273,7 +273,7 @@ def test_feedback_through_actual_controlsd_publication_and_100hz_sender(pipeline
assert core.correction == pytest.approx(expected)
assert core.c1 == pytest.approx(sign*.08+expected_p+expected)
assert controls.ford_path.path_angle == pytest.approx(core.c1, abs=.00025)
assert controls.ford_path.path_offset == pytest.approx(.4)
assert controls.ford_path.path_offset == pytest.approx(sign*.1)
@pytest.mark.parametrize('service_valid', [False, True])
@@ -348,13 +348,13 @@ def test_continuous_pi_reversal_through_selected_limited_request_and_actual_can(
assert wire['LatCtlPath_No_Cs'] == calculate_lat_ctl2_checksum(2, frame % 16, packet[1])
if frame == 199:
assert sign*core.correction < 0. if same_turn else sign*core.correction > 0.
assert controls.ford_path_controller.diagnostics['hypothesis'] == 'model-action-c1-pi-v7'
assert controls.ford_path_controller.diagnostics['hypothesis'] == 'model-action-curvature-c0-pi-v8'
if same_turn:
assert controls.desired_curvature == pytest.approx(sign*.01)
assert sign*controls.ford_path.path_angle >= speed*.01 # No old unwind correction left below the new base.
else:
assert sign*controls.ford_path.path_angle < 0.
assert controls.ford_path.path_offset == pytest.approx(sign*(.2 if same_turn else -.2))
assert controls.ford_path.path_offset == pytest.approx(sign*(.24 if same_turn else -.02))
controls.ford_path_controller.reset()
assert core.c0 == core.c1 == core.correction == 0.
@@ -407,7 +407,7 @@ def test_unwind_and_catchup_through_selected_request_and_actual_can(pipeline, si
assert wire['LatCtlPath_No_Cs'] == calculate_lat_ctl2_checksum(2, frame % 16, packet[1])
assert controls.ford_path.path_angle == pytest.approx(core.correction, abs=.00025)
assert 0. < sign*core.correction < .02
assert controls.ford_path.path_offset == pytest.approx(sign*.05)
assert controls.ford_path.path_offset == pytest.approx(0.)
@pytest.mark.parametrize('sign', [-1., 1.])
@@ -453,10 +453,10 @@ def test_heading_overflow_and_release_through_actual_can(pipeline, sign, fingerp
assert wire['LatCtlPath_No_Cs'] == calculate_lat_ctl2_checksum(2, frame % 16, packet[1])
if frame == 149:
assert controls.desired_curvature == pytest.approx(sign*.1)
assert controls.ford_path.path_offset == pytest.approx(sign*1.6)
assert controls.ford_path.path_offset == pytest.approx(sign*((1-math.cos(.7))/.1+1.4), abs=.005)
assert controls.ford_path.path_angle == pytest.approx(sign*.5)
assert controls.ford_path_controller.diagnostics['offset_overflow'] == pytest.approx(sign*1.4)
assert controls.ford_path.path_offset == pytest.approx(sign*.2)
assert controls.ford_path.path_offset == pytest.approx(sign*(1-math.cos(.28))/.04, abs=.005)
assert controls.ford_path.path_angle == pytest.approx(sign*.28)
assert controls.ford_path_controller.diagnostics['offset_overflow'] == 0.
@@ -25,7 +25,7 @@ def test_feedback_builds_holds_and_unwinds_without_changing_c0(sign):
baseline = tick(matched, sign*.004, sign*.004)
assert controller.correction == pytest.approx(sign*.02)
assert out.path_angle == pytest.approx(sign*.1)
assert out.path_offset == baseline.path_offset == pytest.approx(.4)
assert out.path_offset == baseline.path_offset == pytest.approx(sign*.1)
for _ in range(100):
out = tick(controller, sign*.004, sign*.004)
assert controller.correction == pytest.approx(sign*.02)
@@ -73,7 +73,7 @@ def test_pscm_limit_only_blocks_feedback_further_into_measured_turn(sign):
for _ in range(100):
out = tick(controller, 0., 0., pscm_limited=True)
assert abs(out.path_angle) < .001
assert out.path_offset == pytest.approx(.4)
assert out.path_offset == pytest.approx(0.)
@pytest.mark.parametrize('sign', [-1., 1.])
@@ -1,4 +1,6 @@
"""C1 overflow allocation and release; no assumptions about PSCM response."""
import math
import pytest
from openpilot.selfdrive.controls.lib.ford_model_action import ModelActionController
@@ -7,16 +9,18 @@ from openpilot.selfdrive.controls.tests.test_ford_model_action import make_model
@pytest.mark.parametrize('sign', [-1., 1.])
@pytest.mark.parametrize('speed', [3., 7., 20., 55.])
@pytest.mark.parametrize('heading,offset', [(.4, .2), (.5, .2), (.6, .9), (.8, 2.3)])
def test_clipped_base_heading_preserves_the_seven_metre_reference(sign, speed, heading, offset):
@pytest.mark.parametrize('heading', [.4, .5, .6, .8])
def test_clipped_base_heading_preserves_the_seven_metre_reference(sign, speed, heading):
controller = ModelActionController()
desired = sign*heading/max(7., speed)
for _ in range(150):
out = controller.update(straight(sign*.2), desired, current_curvature=desired, speed=speed, dt=.01)
assert controller.correction == 0.
assert out.path_offset == pytest.approx(sign*offset)
arc = (1-math.cos(7.*desired))/desired
assert controller.c0 == pytest.approx(arc+sign*7.*max(heading-.5, 0.))
assert out.path_offset == pytest.approx(controller.c0, abs=.005)
assert out.path_angle == pytest.approx(sign*min(heading, .5))
assert out.path_offset+7.*out.path_angle == pytest.approx(sign*(.2+7.*heading))
assert out.path_offset+7.*out.path_angle == pytest.approx(arc+sign*7.*heading, abs=.005)
assert out.curvature == out.curvature_rate == 0.
@@ -24,7 +28,7 @@ def test_clipped_base_heading_preserves_the_seven_metre_reference(sign, speed, h
def test_combined_offset_is_clipped_after_allocating_heading(sign):
controller = ModelActionController()
for _ in range(200):
out = controller.update(straight(sign*4.), sign*.1, current_curvature=sign*.1, speed=7., dt=.01)
out = controller.update(straight(sign*4.), sign*.2, current_curvature=sign*.2, speed=7., dt=.01)
assert out.path_offset == pytest.approx(sign*5.11)
assert out.path_angle == pytest.approx(sign*.5)
@@ -35,7 +39,7 @@ def test_overflow_uses_existing_reference_with_short_model_path(sign):
model = make_model([0., 1.], [0., sign*.2], [0., 0.])
for _ in range(150):
out = controller.update(model, sign*.03, current_curvature=sign*.03, speed=20., dt=.01)
assert out.path_offset == pytest.approx(sign*.9)
assert out.path_offset == pytest.approx(sign*((1-math.cos(.21))/.03+.7), abs=.005)
@pytest.mark.parametrize('sign', [-1., 1.])
@@ -43,16 +47,18 @@ def test_extra_offset_releases_at_existing_slew_without_stored_overflow(sign):
controller = ModelActionController()
for _ in range(200):
controller.update(straight(sign*.2), sign*.04, current_curvature=sign*.04, speed=20., dt=.01)
assert controller.c0 == pytest.approx(sign*2.3)
for i in range(60):
start = sign*((1-math.cos(.28))/.04+2.1)
target = sign*(1-math.cos(.14))/.02
assert controller.c0 == pytest.approx(start)
for i in range(70):
before = controller.c0
out = controller.update(straight(sign*.2), sign*.02, current_curvature=sign*.02, speed=20., dt=.01)
assert sign*controller.c0 >= .2-1e-10
assert sign*controller.c0 >= sign*target-1e-10
assert sign*controller.c0 <= sign*before+1e-10
assert abs(controller.c0-before) <= .04+1e-10
if i == 0:
assert controller.c0 == pytest.approx(sign*2.26)
assert out.path_offset == pytest.approx(sign*.2)
assert controller.c0 == pytest.approx(start-sign*.04)
assert out.path_offset == pytest.approx(sign*(1-math.cos(.14))/.02, abs=.005)
assert out.path_angle == pytest.approx(sign*.4)
assert controller.correction == 0.
@@ -64,7 +70,7 @@ def test_c1_feedback_saturation_does_not_spill_correction_into_c0(sign):
out = controller.update(straight(sign*.2), sign*.02, current_curvature=0., speed=20., dt=.01)
assert out.path_angle == pytest.approx(sign*.5)
assert controller.correction == pytest.approx(sign*.1)
assert out.path_offset == pytest.approx(sign*.2)
assert out.path_offset == pytest.approx(sign*(1-math.cos(.14))/.02, abs=.005)
@pytest.mark.parametrize('sign', [-1., 1.])
@@ -74,6 +80,6 @@ def test_overflow_is_base_geometry_with_existing_feedback_gates(sign, enabled, l
for _ in range(150):
out = controller.update(straight(sign*.2), sign*.03, current_curvature=sign*.02, speed=20., dt=.01,
feedback_enabled=enabled, pscm_limited=limited)
assert out.path_offset == pytest.approx(sign*.9)
assert out.path_offset == pytest.approx(sign*((1-math.cos(.21))/.03+.7), abs=.005)
assert out.path_angle == pytest.approx(sign*.5)
assert controller.correction == 0.
@@ -30,7 +30,7 @@ def test_aligned_feedback_does_not_replace_current_feedforward_or_path(sign):
out = controller.update(straight(sign*.4), sign*.01, current_curvature=sign*.008,
feedback_curvature=sign*.008, speed=20., dt=.1)
assert controller.proportional == controller.correction == 0.
assert out.path_offset == pytest.approx(sign*.4)
assert out.path_offset == pytest.approx(sign*(1-math.cos(.07))/.01, abs=.005)
assert out.path_angle == pytest.approx(sign*.2)
# Latest request is ahead of measured steering, but the delay-aligned target
# has already been exceeded. P and I must use the explicit feedback target.
@@ -48,7 +48,7 @@ def test_actual_startup_priority(candidate, observer, fingerprint):
assert type(selected.ford_path_controller) is FordModelActionController
assert selected.ford_path_controller.core.proportional_gain == C1_PROPORTIONAL_GAIN == .50
assert selected.ford_path_controller.core.integral_gain == C1_INTEGRAL_GAIN == .25
assert selected.ford_path_controller.diagnostics['hypothesis'] == 'model-action-c1-pi-v7'
assert selected.ford_path_controller.diagnostics['hypothesis'] == 'model-action-curvature-c0-pi-v8'
else:
assert selected.ford_path_controller is None
assert selected.ford_model_action == candidate
@@ -0,0 +1,84 @@
"""Independent arithmetic and C0-independence stress for production curvature-C0 PI."""
import argparse
import hashlib
import json
import math
from pathlib import Path
from types import SimpleNamespace
import numpy as np
from openpilot.selfdrive.controls.lib import ford_model_action
from tools.ford_pscm_lab.model_action_replay import WireCheck
from openpilot.selfdrive.controls.lib.ford_model_action import ModelActionController
def stress(cycles, output):
wire = WireCheck()
kp, ki = .5, .25
rng = np.random.default_rng(20260913)
core, mirror, other = (ModelActionController() for _ in range(3))
for i in range(cycles):
desired, measured = rng.uniform(-.1, .1, 2)
speed, dt, offset, alternate = rng.uniform(.3, 55.), rng.uniform(.002, .1), *rng.uniform(-8., 8., 2)
active, enabled, limited = i % 211 != 0, i % 97 != 0, i % 7 == 0
feedback_dt = 0. if i % 5 == 0 else rng.uniform(.002, .15)
before = core.c0, core.c1, core.correction
args = {'speed': speed, 'dt': dt, 'feedback_dt': feedback_dt, 'active': active,
'feedback_enabled': enabled, 'pscm_limited': limited}
def model(y):
return SimpleNamespace(position=SimpleNamespace(x=[0., 20.], y=[y, y]), orientation=SimpleNamespace(z=[0., 0.]))
outputs = [core.update(model(offset), desired, current_curvature=measured, **args),
mirror.update(model(-offset), -desired, current_curvature=-measured, **args),
other.update(model(alternate), desired, current_curvature=measured, **args)]
state = np.array([core.c0, core.c1, core.correction, core.proportional])
np.testing.assert_allclose(state, -np.array([mirror.c0, mirror.c1, mirror.correction, mirror.proportional]), atol=1e-10, rtol=0.)
assert (core.c1, core.correction, core.proportional) == (other.c1, other.correction, other.proportional)
if active:
distance = max(7., speed)
base = min(.5, max(-.5, distance*desired))
arc = (1.-math.cos(7.*desired))/desired if abs(desired) > 1e-6 else 24.5*desired
target_c0 = min(5.11, max(-5.11, arc+7.*(distance*desired-base)))
assert abs(core.c0-(before[0]+min(4.*dt, max(-4.*dt, target_c0-before[0])))) <= 1e-10
p = kp*distance*(desired-measured) if enabled else 0.
integral = 0.
if enabled:
increment = ki*(desired-measured)*speed*feedback_dt
direction = measured if measured else before[1]
if limited and increment*direction > 0.:
increment = min(max(increment, min(-before[2], 0.)), max(-before[2], 0.))
integral = before[2]
if increment*integral < 0.:
cancel = math.copysign(min(abs(increment), abs(integral)), increment)
integral += cancel
increment -= cancel
lower, upper = max(-.5, before[1]-.5*dt), min(.5, before[1]+.5*dt)
request = base+p+integral
integral += min(max(increment, min(lower-request, 0.)), max(upper-request, 0.))
assert core.proportional == p and abs(core.correction-integral) <= 1e-10
request = min(.5, max(-.5, base+p+integral))
assert abs(core.c1-(before[1]+min(.5*dt, max(-.5*dt, request-before[1])))) <= 1e-10
assert abs(core.c0) <= 5.11+1e-10 and abs(core.c1) <= .5+1e-10 and abs(core.correction) <= 1.+1e-10
else:
assert np.all(state == 0.)
for command in outputs:
assert command.curvature == command.curvature_rate == 0.
wire.check(command)
result = {'cycles': cycles, 'kp': kp, 'ki': ki, 'controller_updates': 3*cycles,
'can_round_trips': wire.count, 'seed': 20260913, 'independent_c0_comparisons': cycles,
'checks': 'Independent scalar PI/unwind-first/anti-windup arithmetic, mirror symmetry, exact C0 independence, limits, slew, resets, CAN.',
'source_sha256': {str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in
(Path(__file__).resolve(), Path(ford_model_action.__file__).resolve())}}
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(result, indent=2)+'\n')
print(json.dumps(result))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--cycles', type=int, default=20_000)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
stress(args.cycles, args.output)
@@ -0,0 +1,158 @@
"""Circular-arc C0 experiment against the pinned continuous PI v7 controller.
Both controllers run on identical recorded motion. Replace only the encoder's
C0 target, retaining path validity, C1 overflow, continuous PI feedback and
output limits. No onroad selector or production module is modified.
"""
import argparse
from collections import Counter
import hashlib
import math
from pathlib import Path
import json
from types import SimpleNamespace
import numpy as np
from openpilot.selfdrive.controls.lib.ford_path import FordPath
from tools.ford_pscm_lab.feedback_replay import OPENDBC, original_controller
from tools.ford_pscm_lab.model_action_replay import WireCheck, field_checks, sample, table, verify_dependency
BASELINE = '08b3a14ad46260fda0f8d3a1c2cee2d272504153'
GAIN = .50
STATION = 7.
def arc_offset(curvature):
"""Circular-arc y at 7 m arc length, starting at zero position/heading.
Equivalent to (1-cos(7*k))/k; sinc avoids cancellation near zero.
This is reference geometry, not an inverse PSCM response model.
"""
half_heading = .5*STATION*curvature
sinc = math.sin(half_heading)/half_heading if half_heading else 1.
return .5*curvature*STATION**2*sinc**2
def make_controller(curvature_c0=False):
# original_controller creates fresh, isolated module globals each time.
# Patching this clone cannot alter production or the other replay controller.
original, source_hash = original_controller(BASELINE)
controller = type(original)(proportional_gain=GAIN)
if curvature_c0:
namespace = controller.core.update.__globals__
original_encoder = namespace['encode_model_action']
def encoder(model, desired_curvature, speed):
target = original_encoder(model, desired_curvature, speed)
if not target.valid:
return target
return FordPath(True, arc_offset(desired_curvature), target.path_angle, 0., 0.)
namespace['encode_model_action'] = encoder
controller.hypothesis = 'curvature-c0-continuous-pi-offline-v1'
controller.reset()
return controller, source_hash
def replay(directory, output):
directory, output = directory.resolve(), output.resolve()
if output == directory or directory in output.parents:
raise ValueError('Output must be outside the source route directory')
verify_dependency(OPENDBC)
metadata = json.loads((directory/'metadata.json').read_text())
with np.load(directory/'route.npz', allow_pickle=False) as z:
r = {k: table(z, k) for k in ('controls', 'cs', 'cc', 'model', 'params', 'pscm')}
if 'maneuver' in z and len(z['maneuver']):
raise ValueError('Selected maneuver publications require a separate reference reconstruction')
with np.load(directory/'model_paths.npz', allow_pickle=False) as z:
model_ns, paths = z['ns'], z['paths']
c, t = r['controls'], r['controls']['t']
assert all(np.all(np.diff(stream['t']) >= 0.) for stream in r.values())
cs, pa, ps = (sample(r[k], t) for k in ('cs', 'params', 'pscm'))
cc = sample(r['cc'], t, nearest=True)
mi = np.clip(np.searchsorted(r['model']['ns'], c['model_ns']), 0, len(r['model']['ns'])-1)
exact = r['model']['ns'][mi] == c['model_ns']
np.testing.assert_array_equal(model_ns, r['model']['ns'])
models = [SimpleNamespace(position=SimpleNamespace(x=p[0], y=p[1]), orientation=SimpleNamespace(z=p[2])) for p in paths]
baseline, baseline_hash = make_controller()
candidate, candidate_hash = make_controller(True)
assert baseline_hash == candidate_hash
controllers = (baseline, candidate)
shape = (2, len(t))
commands = np.zeros((*shape, 4))
valid = np.zeros(shape, bool)
names = ('heading_proportional', 'heading_correction', 'heading_feedforward', 'feedback_enabled',
'offset_overflow', 'pscm_limited', 'pscm_status_fresh')
diagnostic = {name: np.zeros(shape) for name in names}
status_counts = [Counter(), Counter()]
wire = WireCheck()
for i, now in enumerate(t):
model_time = r['model']['t'][mi[i]]
service_valid = bool(c['valid'][i] and cc['valid'][i] and cs['valid'][i] and cs['can_valid'][i]
and pa['valid'][i] and r['model']['valid'][mi[i]] and exact[i]
and abs(cc['t'][i]-now) < .005 and 0. <= now-pa['t'][i] <= .15)
model = models[mi[i]] if exact[i] else None
status = SimpleNamespace(valid=bool(ps['valid'][i] and ps['status_valid'][i]), canMonoTime=round(ps['stamp'][i]*1e9),
limit=int(ps['limit'][i]), lateralState=int(ps['lateral_state'][i]), denied=bool(ps['denied'][i]))
kwargs = {'speed': cs['speed'][i], 'yaw_rate': cs['yaw'][i], 'now': now, 'measurement_time': cs['t'][i],
'model_time': model_time, 'reference_time': model_time, 'active': bool(cc['active'][i]), 'valid': service_valid,
'current_curvature': c['measured'][i], 'driver_pressed': bool(cs['pressed'][i]),
'driver_torque': cs['torque'][i], 'pscm_status': status}
for k, controller in enumerate(controllers):
command = controller.update(model, c['desired'][i], **kwargs)
commands[k, i] = command.path_offset, command.path_angle, command.curvature, command.curvature_rate
valid[k, i] = command.valid
d = controller.diagnostics
status_counts[k][d['status']] += 1
for name in names:
diagnostic[name][k, i] = d.get(name, 0.)
wire.check(command)
np.testing.assert_array_equal(valid[0], valid[1])
for name in ('heading_proportional', 'heading_feedforward', 'feedback_enabled', 'offset_overflow', 'pscm_limited', 'pscm_status_fresh'):
np.testing.assert_array_equal(diagnostic[name][0], diagnostic[name][1])
assert status_counts[0] == status_counts[1]
np.testing.assert_array_equal(commands[0, :, 1:], commands[1, :, 1:])
np.testing.assert_array_equal(diagnostic['heading_correction'][0], diagnostic['heading_correction'][1])
for k in range(2):
field_checks(commands[k], valid[k], t)
assert np.all(abs(diagnostic['heading_correction'][k]) <= 1.+1e-10)
assert np.all(diagnostic['heading_correction'][k, diagnostic['feedback_enabled'][k] == 0.] == 0.)
with np.load(directory/'angles.npz') as a:
np.testing.assert_allclose(a['t'], t-metadata['t0'], atol=1e-8, rtol=0.)
clean = a['clean'] & valid[0] & (diagnostic['feedback_enabled'][0] != 0.)
weight = a['weight']
report = {'scope': __doc__, 'baseline_revision': BASELINE, 'baseline_source_sha256': baseline_hash,
'opendbc_revision': OPENDBC, 'proportional_gain': GAIN, 'integral_gain': .25, 'station_m': STATION,
'cycles': len(t), 'controller_updates': 2*len(t), 'active_cycles_per_controller': int(valid[0].sum()),
'can_round_trips': wire.count, 'status_counts': dict(status_counts[0]),
'same_activation_p_feedforward_overflow_and_feedback_gates': True,
'changed_c1_cycles': int(np.sum(commands[0, :, 1] != commands[1, :, 1])),
'max_abs_c1_change_rad': float(abs(commands[0, :, 1]-commands[1, :, 1]).max()),
'changed_integral_cycles': int(np.sum(diagnostic['heading_correction'][0] != diagnostic['heading_correction'][1])),
'pscm_status_fresh_active_seconds': float(weight[valid[0] & (diagnostic['pscm_status_fresh'][0] != 0.)].sum()),
'clean_seconds': float(weight[clean].sum()), 'car': metadata['car'], 'identities': metadata['identities'],
'source_sha256': {str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in
(directory/'route.npz', directory/'model_paths.npz', directory/'metadata.json',
directory/'angles.npz', Path(__file__).resolve())},
'limitations': ['No new steering trace or physical tracking score: recorded motion stays fixed.',
'The baseline is v7 on every route, regardless of its recorded controller version.',
'C1 and integral state match exactly on fixed recorded motion; physical feedback may differ.',
'Publication time proxies computation time; full SubMaster state is unavailable.',
'The synthetic arc keeps the original model-health gates for a controlled comparison.']}
output.mkdir(parents=True, exist_ok=True)
np.savez_compressed(output/'commands.npz', t=t-metadata['t0'], commands=commands, valid=valid, clean=clean, weight=weight,
desired_curvature=c['desired'], measured_curvature=c['measured'], desired_angle=c['desired_angle'],
actual_angle=c['actual_angle'], speed=cs['speed'], **diagnostic)
(output/'report.json').write_text(json.dumps(report, indent=2, allow_nan=False)+'\n')
print(json.dumps({k: report[k] for k in ('cycles', 'can_round_trips', 'changed_c1_cycles', 'max_abs_c1_change_rad', 'clean_seconds')}))
return report
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('directory', type=Path)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
replay(args.directory, args.output)
@@ -0,0 +1,110 @@
"""Verify production commands against the archived curvature-C0 offline candidate.
Fixed recorded motion verifies integration parity, not physical tracking.
"""
import argparse
from concurrent.futures import ProcessPoolExecutor, as_completed
import hashlib
import json
from pathlib import Path
from types import SimpleNamespace
import numpy as np
from openpilot.selfdrive.controls.lib import ford_model_action
from openpilot.selfdrive.controls.lib.ford_model_action import select_model_action_controller
from opendbc.car.ford.values import FordFlags
from tools.ford_pscm_lab.feedback_replay import OPENDBC
from tools.ford_pscm_lab.model_action_replay import WireCheck, field_checks, sample, table, verify_dependency
ROUTES = ('112', '113', '114', '115', '116', '117', 'a0', 'a2', 'a5', 'a9', 'b8', 'b9', 'ca', 'raptor02')
SELECTED = 1
def replay(label, output):
directory = Path('.cache/ford_raptor_route02' if label == 'raptor02' else f'.cache/ford_route{label}').resolve()
previous = Path('.cache/ford_curvature_c0_v7')/label
output = (output/label).resolve()
if output == directory or directory in output.parents or output == previous.resolve():
raise ValueError('Output must preserve previous experiments and input extracts')
verify_dependency(OPENDBC)
archived_report = json.loads((previous/'report.json').read_text())
assert archived_report['baseline_revision'] == '08b3a14ad46260fda0f8d3a1c2cee2d272504153'
assert archived_report['changed_c1_cycles'] == archived_report['changed_integral_cycles'] == 0
for filename in ('route.npz', 'model_paths.npz', 'metadata.json'):
path = directory/filename
assert hashlib.sha256(path.read_bytes()).hexdigest() == archived_report['source_sha256'][str(path)]
archived = dict(np.load(previous/'commands.npz', allow_pickle=False))
metadata = json.loads((directory/'metadata.json').read_text())
with np.load(directory/'route.npz', allow_pickle=False) as raw:
r = {key: table(raw, key) for key in ('controls', 'cs', 'cc', 'model', 'params', 'pscm')}
if 'maneuver' in raw and len(raw['maneuver']):
raise ValueError('Maneuver reference reconstruction is not available in this sweep')
with np.load(directory/'model_paths.npz', allow_pickle=False) as raw:
model_ns, paths = raw['ns'], raw['paths']
c, t = r['controls'], r['controls']['t']
assert all(np.all(np.diff(stream['t']) >= 0.) for stream in r.values())
np.testing.assert_allclose(archived['t'], t-metadata['t0'], rtol=0., atol=1e-8)
cs, pa, ps = (sample(r[key], t) for key in ('cs', 'params', 'pscm'))
cc = sample(r['cc'], t, nearest=True)
mi = np.clip(np.searchsorted(r['model']['ns'], c['model_ns']), 0, len(r['model']['ns'])-1)
exact = r['model']['ns'][mi] == c['model_ns']
np.testing.assert_array_equal(model_ns, r['model']['ns'])
models = [SimpleNamespace(position=SimpleNamespace(x=p[0], y=p[1]), orientation=SimpleNamespace(z=p[2])) for p in paths]
cp = SimpleNamespace(brand='ford', flags=int(FordFlags.CANFD))
assert select_model_action_controller(cp, False) is None
controller = select_model_action_controller(cp, True)
assert controller is not None
assert (controller.core.proportional_gain, controller.core.integral_gain) == (.5, .25)
shape = (len(t),)
commands = np.zeros((*shape, 4))
valid = np.zeros(shape, bool)
names = ('heading_proportional', 'heading_correction', 'heading_feedforward', 'feedback_enabled', 'offset_overflow', 'pscm_limited')
diagnostics = {name: np.zeros(shape) for name in names}
wire = WireCheck()
for i, now in enumerate(t):
model_time = r['model']['t'][mi[i]]
services_valid = bool(c['valid'][i] and cc['valid'][i] and cs['valid'][i] and cs['can_valid'][i]
and pa['valid'][i] and r['model']['valid'][mi[i]] and exact[i]
and abs(cc['t'][i]-now) < .005 and 0. <= now-pa['t'][i] <= .15)
model = models[mi[i]] if exact[i] else None
status = SimpleNamespace(valid=bool(ps['valid'][i] and ps['status_valid'][i]), canMonoTime=round(ps['stamp'][i]*1e9),
limit=int(ps['limit'][i]), lateralState=int(ps['lateral_state'][i]), denied=bool(ps['denied'][i]))
kwargs = {'speed': cs['speed'][i], 'yaw_rate': cs['yaw'][i], 'now': now, 'measurement_time': cs['t'][i],
'model_time': model_time, 'reference_time': model_time, 'active': bool(cc['active'][i]), 'valid': services_valid,
'current_curvature': c['measured'][i], 'driver_pressed': bool(cs['pressed'][i]),
'driver_torque': cs['torque'][i], 'pscm_status': status}
command = controller.update(model, c['desired'][i], **kwargs)
commands[i] = command.path_offset, command.path_angle, command.curvature, command.curvature_rate
valid[i] = command.valid
for name in names:
diagnostics[name][i] = controller.diagnostics.get(name, 0.)
wire.check(command)
field_checks(commands, valid, t)
np.testing.assert_array_equal(commands, archived['commands'][SELECTED])
np.testing.assert_array_equal(valid, archived['valid'][SELECTED])
for name in names:
np.testing.assert_array_equal(diagnostics[name], archived[name][SELECTED])
output.mkdir(parents=True, exist_ok=True)
sources = (directory/'route.npz', directory/'model_paths.npz', directory/'metadata.json',
previous/'report.json', previous/'commands.npz', Path(__file__).resolve(), Path(ford_model_action.__file__).resolve())
result = {'scope': __doc__, 'route': label, 'cycles': len(t), 'can_round_trips': wire.count,
'production_matches_archived_trial_exactly': True, 'opendbc_revision': OPENDBC,
'hypothesis': controller.hypothesis, 'kp': .5, 'ki': .25,
'source_sha256': {str(path.resolve()): hashlib.sha256(path.read_bytes()).hexdigest() for path in sources}}
(output/'report.json').write_text(json.dumps(result, indent=2, allow_nan=False)+'\n')
return result
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--routes', nargs='+', choices=ROUTES, default=ROUTES)
parser.add_argument('--output', type=Path, required=True)
parser.add_argument('--workers', type=int, default=4)
args = parser.parse_args()
with ProcessPoolExecutor(max_workers=args.workers) as pool:
jobs = {pool.submit(replay, label, args.output): label for label in args.routes}
for job in as_completed(jobs):
result = job.result()
print(json.dumps({'completed': jobs[job], 'cycles': result['cycles'], 'can_checks': result['can_round_trips']}), flush=True)