mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-09-08 13:53:41 +08:00
Ford: use full geometric prediction within existing command limits
Remove the hand-chosen 0.15 m / 25% cap on the prediction adjustment. Retain the available-horizon bound, nonfinite fallback, total field limits, yaw damping, slew, input gates, two states and zero C2/C3. Cover full predictions and geometric countersteering in regression tests. Validate 374 tests plus 26 subtests, four-route replay and 817,346 Float32/CAN round trips. Document command changes without inferring physical tracking performance from PSCM limits or fixed-input replay. Assisted-by: OpenAI Codex
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# Experimental Ford offset damping, v2
|
||||
|
||||
This document and its validation counts describe the archived v2 source. The
|
||||
[current v3 experiment](ford_model_action_prediction.md) adds bounded path prediction.
|
||||
[current v4 experiment](ford_model_action_full_prediction.md) uses full path prediction.
|
||||
|
||||
Segment 10 of the supplied route9b recording shows measured turning persisting
|
||||
as requested right curvature falls. At about 643.0 s, before strong driver
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Ford selected-action drive-test branch
|
||||
|
||||
The candidate is selectable on the **Ford CAN FD F-150 Lightning** behind
|
||||
its own persistent, default-off Sunnylink toggle. Version 3 adds
|
||||
[bounded geometric path prediction](ford_model_action_prediction.md) while retaining
|
||||
its own persistent, default-off Sunnylink toggle. Version 4 uses
|
||||
[full geometric path prediction](ford_model_action_full_prediction.md) while retaining
|
||||
[excess-yaw offset damping](ford_model_action_damping.md). Input gates are unchanged.
|
||||
`calibration_approved=false`: offline checks do not establish physical tracking,
|
||||
turn-exit behavior or closed-loop stability.
|
||||
@@ -20,7 +20,7 @@ turn-exit behavior or closed-loop stability.
|
||||
|
||||
The startup log event `Ford path controller selected` should report
|
||||
`FordModelActionController`. Periodic `Ford C2-free path tracking` events
|
||||
identify `hypothesis=model-action-c0-c1-prediction-v3` and report host yaw and the command tuple.
|
||||
identify `hypothesis=model-action-c0-c1-prediction-v4` and report host yaw and the command tuple.
|
||||
|
||||
Turning the new toggle off and completing another offroad-to-onroad cycle
|
||||
restores **PSCM Coefficient Observer** if selected, otherwise the original
|
||||
@@ -54,9 +54,10 @@ returns separate strings owned by the parameter handle. Regression tests
|
||||
check distinct registered keys across flags, and toggle tests check its
|
||||
persistence and backup registration using the rebuilt native library.
|
||||
|
||||
The current validation record is `ford_model_action_prediction_validation.json`;
|
||||
the [prediction notes](ford_model_action_prediction.md) explain the command changes
|
||||
and remaining physical uncertainty. `ford_model_action_damping_validation.json`
|
||||
The current validation record is `ford_model_action_full_prediction_validation.json`;
|
||||
the [full-prediction notes](ford_model_action_full_prediction.md) explain cap removal
|
||||
and remaining physical uncertainty. `ford_model_action_prediction_validation.json`
|
||||
archives the capped v3 evaluation. `ford_model_action_damping_validation.json`
|
||||
archives the preceding v2 checks at their recorded source hashes.
|
||||
`ford_model_action_drive_test_validation.json` archives v1 wiring validation
|
||||
at the recorded source hashes, including 284 tests and 26 subtests. Its counts
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# Experimental Ford full path prediction, v4
|
||||
|
||||
V4 removes the extra 15 cm / 25% limit on the geometric prediction introduced
|
||||
in [v3](ford_model_action_prediction.md). Those numbers were hand-chosen tuning
|
||||
bounds, not identified Ford response limits. The current user request is to
|
||||
remove that restriction; the existing default-off Sunnylink toggle remains.
|
||||
|
||||
The controller now uses the full predicted offset from the same model path,
|
||||
assuming 150 ms of motion along the selected, upstream-limited curvature. The
|
||||
150 ms horizon remains an engineering assumption. Available model geometry
|
||||
still limits the prediction distance, with endpoint hold for short paths and
|
||||
fallback to the valid base offset if prediction arithmetic is nonfinite.
|
||||
|
||||
The existing total command limits (C0 ±5.11 m, C1 ±0.5 rad), independent slew
|
||||
rates (4 m/s, 0.5 rad/s), quantization, yaw damping, input/service gates and
|
||||
zero C2/C3 remain unchanged. Only two control states persist. No integrator,
|
||||
model history, extra toggle or PSCM feedback loop is added.
|
||||
|
||||
Removing the adjustment cap also permits the predicted C0 to oppose the
|
||||
original offset or become nonzero from a zero original offset. For example,
|
||||
a straight path with a nonzero selected turn request can have an opposing
|
||||
future-frame offset. Tests cover that behavior, mirrored turn releases,
|
||||
return to zero, and unchanged slew; sign preservation of the original C0 is
|
||||
no longer claimed. The yaw damper still cannot reverse its input target.
|
||||
|
||||
## Evidence and interpretation
|
||||
|
||||
The PSCM reports a generic `LimitReached` state. It does not tell us whether
|
||||
an incoming target is geometrically correct or well timed. Its internal
|
||||
limits cannot establish the tracking performance of this predictor. Removal
|
||||
is an experiment supported by command comparisons, not by an assumption that
|
||||
the PSCM will correct an excessive or mistimed request.
|
||||
|
||||
Compared with capped v3 on identical recorded inputs:
|
||||
|
||||
| Interval | Effect of removing the extra cap |
|
||||
| --- | --- |
|
||||
| Latest tight-left entry, 173–175.4 s | Mean C0 magnitude +0.032 m, maximum change 0.07 m |
|
||||
| Earlier right entry, 637–640 s | Mean magnitude +0.028 m, maximum change 0.10 m |
|
||||
| Earlier right exit, 642.7–643.852 s | All 115 commands unchanged |
|
||||
| Driver-clean low requests above 8 m/s, routes9b/9e | Mean absolute change 0.0018 / 0.0014 m; maximum 0.02 m |
|
||||
| All eligible samples on either recent route | Maximum absolute command change 0.13 m |
|
||||
|
||||
C1 and command eligibility are exactly identical to v3 on both recent routes.
|
||||
Some command signs change near zero: this is an intended consequence of using
|
||||
the full transform, not proof those corrections improve driving. Entry windows
|
||||
include driver input, reported in the validation record. The magnitude changes
|
||||
above describe controller C0, not measured lateral vehicle displacement.
|
||||
|
||||
The earlier v1 archive is also reproduced exactly on routes90/95, separately
|
||||
from the current controller pass. All replay uses original timestamps;
|
||||
publication times proxy computation, exact consumed model frames and causal
|
||||
carState are retained, and complete SubMaster health is unavailable. The
|
||||
recorded model and vehicle motion remain fixed. There is no measured physical
|
||||
improvement, stability result or new desired-versus-actual steering trajectory.
|
||||
|
||||
Final validation passes 374 tests and 26 subtests with 100% controller statement
|
||||
and branch coverage, 299,604 original route cycles and 817,346 Float32/CAN
|
||||
round trips. The controller is 190 total lines / 122 code lines excluding
|
||||
blanks, comments and docstrings; two control states persist.
|
||||
|
||||
See `ford_model_action_full_prediction_validation.json` for final test counts,
|
||||
coverage, dependency pins, source hashes and route/packing results. The initial
|
||||
uncapped variant was evaluated in a separate lab file before editing production;
|
||||
final route checks execute the production v4 source.
|
||||
|
||||
## Reproduce and select
|
||||
|
||||
Use the dependencies and combined suite command in the
|
||||
[drive-test guide](ford_model_action_drive_test.md). For the recent routes:
|
||||
|
||||
```sh
|
||||
python -m tools.ford_pscm_lab.damping_replay /path/to/route9e/rlogs --baseline v3 --candidate current --window left_entry 173 175.4 --window left_peak 175.4 178.3 --window left_exit 178.3 180.5 --window reversal 728 734 --output /path/to/separate/route9e-results
|
||||
python -m tools.ford_pscm_lab.damping_replay /path/to/route9b/rlogs --baseline v3 --candidate current --window right_entry 637 640 --window right_exit 642.7 643.852 --output /path/to/separate/route9b-results
|
||||
python -m tools.ford_pscm_lab.stress_model_action --cycles 200000 --seed 20260907 --opendbc-revision c21a9013700734dd20b09e05aa68329ad8cc20f9 --output /path/to/stress.json
|
||||
```
|
||||
|
||||
The same **Selected-Action Path Tracking (Experimental)** toggle selects v4
|
||||
on the CAN FD F-150 Lightning. An installation with the toggle already enabled
|
||||
selects v4 after updating and restarting controlsd. Diagnostics identify
|
||||
`model-action-c0-c1-prediction-v4`; `calibration_approved=false` remains explicit.
|
||||
Deployment branch: `sunnypilot/sunnypilot`, `hiimisaac-dev`. This work does not
|
||||
install software on the device or change its settings.
|
||||
@@ -0,0 +1,799 @@
|
||||
{
|
||||
"date": "2026-09-07",
|
||||
"baseline_commit": "01f8d51c82b3e863f1012d383b5994813ef01b81",
|
||||
"hypothesis": "model-action-c0-c1-prediction-v4",
|
||||
"scope": "Removal of only the extra prediction adjustment cap; no physical tracking or stability claim.",
|
||||
"deployment_target": {
|
||||
"repository": "sunnypilot/sunnypilot",
|
||||
"branch": "hiimisaac-dev"
|
||||
},
|
||||
"calibration_approved": false,
|
||||
"hardware_build_and_device_boot": "not performed",
|
||||
"controller_size": {
|
||||
"total_lines": 190,
|
||||
"code_lines_excluding_blanks_comments_docstrings": 122,
|
||||
"core_persistent_values": 2,
|
||||
"adapter_timestamps": 3
|
||||
},
|
||||
"checks": {
|
||||
"combined_ford_params_sunnylink_suite": "374 passed, 26 subtests passed; no skips",
|
||||
"coverage": {
|
||||
"covered_lines": 113,
|
||||
"num_statements": 113,
|
||||
"percent_covered": 100.0,
|
||||
"percent_covered_display": "100",
|
||||
"missing_lines": 0,
|
||||
"excluded_lines": 0,
|
||||
"percent_statements_covered": 100.0,
|
||||
"percent_statements_covered_display": "100",
|
||||
"num_branches": 32,
|
||||
"num_partial_branches": 0,
|
||||
"covered_branches": 32,
|
||||
"missing_branches": 0,
|
||||
"percent_branches_covered": 100.0,
|
||||
"percent_branches_covered_display": "100"
|
||||
},
|
||||
"ruff": "pass",
|
||||
"ty_controller_and_lab": "pass",
|
||||
"settings_compiler_check": "pass",
|
||||
"cap_removal_red_probe": "11 tests fail with the v3 cap present; all 40 prediction tests pass after removal.",
|
||||
"standards_review_remaining_findings": 0,
|
||||
"spec_review_remaining_findings": 0,
|
||||
"independent_review_verification": "Each reviewer passed 172 focused tests and verified source/artifact hashes and route/packing totals."
|
||||
},
|
||||
"stress": {
|
||||
"seed": 20260907,
|
||||
"random_cycles": 200000,
|
||||
"mirrored_core_updates": 200000,
|
||||
"invalid_or_inactive_resets": 3537,
|
||||
"field_boundary_cases": 18138,
|
||||
"float32_can_round_trips": 218138,
|
||||
"analytic_targets_scalar_slew_and_mirror_checks_pass": true,
|
||||
"bounded_excess_yaw_damping_checked": true,
|
||||
"full_geometric_prediction_checked": true,
|
||||
"direct_raw_float32_packing_matches_host_output": true,
|
||||
"max_continuous_step_c0_c1": [
|
||||
0.400000000000329,
|
||||
0.05000000000000002
|
||||
],
|
||||
"calibration_approved": false,
|
||||
"scope": "Numerical construction only; no PSCM response or closed-loop performance claims.",
|
||||
"opendbc_import_head": "c21a9013700734dd20b09e05aa68329ad8cc20f9",
|
||||
"source_sha256": {
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/stress_model_action.py": "66adef6cba120a1a3d8e9730ea987ef49f8fe283023362dbb99608dee0e3229f",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/model_action_replay.py": "7cddac7ce9e88cc32bc7afbec7f9df79b66bb4dfa2fa5f9b36654891fae23a19"
|
||||
}
|
||||
},
|
||||
"routes": {
|
||||
"route90": {
|
||||
"scope": "Command construction and adapter reconstruction only; no counterfactual closed-loop score.",
|
||||
"calibration_approved": false,
|
||||
"executes_live_selector": false,
|
||||
"cycles": 78812,
|
||||
"core_active_cycles": 73055,
|
||||
"core_exact_archived_match": true,
|
||||
"cohorts_reproduced": true,
|
||||
"adapter_active_cycles": 73055,
|
||||
"adapter_status_counts": {
|
||||
"inactive": 5757,
|
||||
"active": 73055
|
||||
},
|
||||
"adapter_matches_current_core_with_yaw_and_fresh_engagement_dt": true,
|
||||
"core_active_path_shorter_than_7m_cycles": 0,
|
||||
"adapter_validity_differs_from_archive_cycles": 0,
|
||||
"adapter_command_differs_from_archive_cycles": 42170,
|
||||
"adapter_max_absolute_command_difference_c0_c1": [
|
||||
0.33999999999999986,
|
||||
0.0020000000000000018
|
||||
],
|
||||
"field_slew_zero_c2_c3_pass": true,
|
||||
"float32_can_round_trips": 157624,
|
||||
"timing": "Original controls publication timestamps proxy computation time; repeated frames and gaps retained. No identified delay.",
|
||||
"eligibility": "Adapter checks recorded services independently; full SubMaster health is unavailable. Core uses archived validity.",
|
||||
"reference": "Recorded controlsState.desiredCurvature, already selected/limited. These two routes have no maneuver publications.",
|
||||
"host_yaw": "Extract cs.yaw equals -carState.yawRate; current adapter uses it for bounded damping.",
|
||||
"archived_core_revision": "5fc16abc7662020706e29f57d31a6d5e2bc1293a",
|
||||
"archived_core_source_sha256": "8f3bc5d68e0051776f614a2ccffae84a88f7898dc95bdc12c23dcfe10dfe676a",
|
||||
"cohorts": {
|
||||
"small_request": {
|
||||
"seconds": 475.24161910000026,
|
||||
"core_c0_c1_rms": [
|
||||
0.06125184365338734,
|
||||
0.0057692154738982855
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.057970374224682646,
|
||||
0.007060795713496332
|
||||
],
|
||||
"adapter_eligible_seconds": 475.24161910000026,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.0666350702799088,
|
||||
0.0057692154738982855
|
||||
]
|
||||
},
|
||||
"turn": {
|
||||
"seconds": 47.85871389900012,
|
||||
"core_c0_c1_rms": [
|
||||
0.18725251016049418,
|
||||
0.046449510236699354
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.4773534845264775,
|
||||
0.04315935101392594
|
||||
],
|
||||
"adapter_eligible_seconds": 47.85871389900012,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.21255148222786066,
|
||||
0.046449510236699354
|
||||
]
|
||||
},
|
||||
"small_speed_2_8": {
|
||||
"seconds": 38.696717235999785,
|
||||
"core_c0_c1_rms": [
|
||||
0.209264275431127,
|
||||
0.014019080484620104
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.1593558816527209,
|
||||
0.01823849401244768
|
||||
],
|
||||
"adapter_eligible_seconds": 38.696717235999785,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.22422047076469706,
|
||||
0.014019080484620104
|
||||
]
|
||||
},
|
||||
"turn_speed_2_8": {
|
||||
"seconds": 0.30159887100000304,
|
||||
"core_c0_c1_rms": [
|
||||
2.090789090165622,
|
||||
0.21878942570759013
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
2.155728831937131,
|
||||
0.38375302254248855
|
||||
],
|
||||
"adapter_eligible_seconds": 0.30159887100000304,
|
||||
"adapter_c0_c1_rms": [
|
||||
2.2977717861671105,
|
||||
0.21878942570759013
|
||||
]
|
||||
},
|
||||
"small_speed_8_15": {
|
||||
"seconds": 79.2599167590001,
|
||||
"core_c0_c1_rms": [
|
||||
0.02250905681378238,
|
||||
0.005843904113223639
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.03477379324171293,
|
||||
0.006545440153133947
|
||||
],
|
||||
"adapter_eligible_seconds": 79.2599167590001,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.027461818062006878,
|
||||
0.005843904113223639
|
||||
]
|
||||
},
|
||||
"turn_speed_8_15": {
|
||||
"seconds": 0.022264622000001566,
|
||||
"core_c0_c1_rms": [
|
||||
0.20999999999999996,
|
||||
0.04349999999999998
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.25,
|
||||
0.04050000011920929
|
||||
],
|
||||
"adapter_eligible_seconds": 0.022264622000001566,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.20999999999999996,
|
||||
0.04349999999999998
|
||||
]
|
||||
},
|
||||
"small_speed_15_55": {
|
||||
"seconds": 357.2849851050004,
|
||||
"core_c0_c1_rms": [
|
||||
0.011622097035118838,
|
||||
0.003925571829874466
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.03809717934501699,
|
||||
0.0045587590980714285
|
||||
],
|
||||
"adapter_eligible_seconds": 357.2849851050004,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.017138026451004092,
|
||||
0.003925571829874466
|
||||
]
|
||||
},
|
||||
"turn_speed_15_55": {
|
||||
"seconds": 47.53485040600012,
|
||||
"core_c0_c1_rms": [
|
||||
0.08686835454709245,
|
||||
0.043216347944464766
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.4471065308273131,
|
||||
0.030663943784303503
|
||||
],
|
||||
"adapter_eligible_seconds": 47.53485040600012,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.10939067791228956,
|
||||
0.043216347944464766
|
||||
]
|
||||
},
|
||||
"pose_quiet": {
|
||||
"seconds": 427.99642020700026,
|
||||
"core_c0_c1_rms": [
|
||||
0.01261133542366393,
|
||||
0.00422389167351435
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.03724956081568198,
|
||||
0.0046880581807907966
|
||||
],
|
||||
"adapter_eligible_seconds": 427.99642020700026,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.017590935519277658,
|
||||
0.00422389167351435
|
||||
]
|
||||
}
|
||||
},
|
||||
"workspace_head": "01f8d51c82b3e863f1012d383b5994813ef01b81",
|
||||
"opendbc_import_head": "72a775d35e54c21ff5c5798acef22016eedcc0a7",
|
||||
"opendbc_import_path": "/Users/ibpersonal/.codex/worktrees/b926/sunnypilot/opendbc_repo",
|
||||
"source_sha256": {
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/model_action_replay.py": "7cddac7ce9e88cc32bc7afbec7f9df79b66bb4dfa2fa5f9b36654891fae23a19",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_path.py": "383538fc7cdae3bc28dffb71fe12ac5f3f9866ffbe6adfb7457f3593e9fc903a",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route90/route.npz": "51e8c26eedde253e171af47d704c1967ba45ae6825d883393bec1fb9e00251c1",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route90/metadata.json": "742afec55ed629155b22d387f376f878f7b2765221fe94f515a829052bdd916f",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route90/encoder_comparison.npz": "7a625d3ed5cbd8013d1028aa3bc421740551dcae5c3d60981208bd047af9794c",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route90/encoder_comparison.json": "64e1cc4be84394ac7ec408d383b99b48ada9b3fd129cdef2fd846e5fac620d66",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route90/pose_candidate/pose_replay.npz": "4457ccc0868354749da5b72c1dea0faf750f783dfdc87038101288fdcba1e707"
|
||||
}
|
||||
},
|
||||
"route95": {
|
||||
"scope": "Command construction and adapter reconstruction only; no counterfactual closed-loop score.",
|
||||
"calibration_approved": false,
|
||||
"executes_live_selector": false,
|
||||
"cycles": 54738,
|
||||
"core_active_cycles": 37614,
|
||||
"core_exact_archived_match": true,
|
||||
"cohorts_reproduced": true,
|
||||
"adapter_active_cycles": 37614,
|
||||
"adapter_status_counts": {
|
||||
"inactive": 17124,
|
||||
"active": 37614
|
||||
},
|
||||
"adapter_matches_current_core_with_yaw_and_fresh_engagement_dt": true,
|
||||
"core_active_path_shorter_than_7m_cycles": 44,
|
||||
"adapter_validity_differs_from_archive_cycles": 0,
|
||||
"adapter_command_differs_from_archive_cycles": 25912,
|
||||
"adapter_max_absolute_command_difference_c0_c1": [
|
||||
0.2999999999999998,
|
||||
0.0010000000000000009
|
||||
],
|
||||
"field_slew_zero_c2_c3_pass": true,
|
||||
"float32_can_round_trips": 109476,
|
||||
"timing": "Original controls publication timestamps proxy computation time; repeated frames and gaps retained. No identified delay.",
|
||||
"eligibility": "Adapter checks recorded services independently; full SubMaster health is unavailable. Core uses archived validity.",
|
||||
"reference": "Recorded controlsState.desiredCurvature, already selected/limited. These two routes have no maneuver publications.",
|
||||
"host_yaw": "Extract cs.yaw equals -carState.yawRate; current adapter uses it for bounded damping.",
|
||||
"archived_core_revision": "5fc16abc7662020706e29f57d31a6d5e2bc1293a",
|
||||
"archived_core_source_sha256": "8f3bc5d68e0051776f614a2ccffae84a88f7898dc95bdc12c23dcfe10dfe676a",
|
||||
"cohorts": {
|
||||
"small_request": {
|
||||
"seconds": 160.93427628100014,
|
||||
"core_c0_c1_rms": [
|
||||
0.027283284164810895,
|
||||
0.006054573235641941
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.04189890174428264,
|
||||
0.0075342351227490395
|
||||
],
|
||||
"adapter_eligible_seconds": 160.93427628100014,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.03232621215993653,
|
||||
0.006054573235641941
|
||||
]
|
||||
},
|
||||
"turn": {
|
||||
"seconds": 58.39837079799993,
|
||||
"core_c0_c1_rms": [
|
||||
0.5425346915921071,
|
||||
0.09102985199238507
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.63700479987207,
|
||||
0.09691236120507696
|
||||
],
|
||||
"adapter_eligible_seconds": 58.39837079799993,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.5674989495695087,
|
||||
0.09102985199238507
|
||||
]
|
||||
},
|
||||
"small_speed_2_8": {
|
||||
"seconds": 8.083969085000064,
|
||||
"core_c0_c1_rms": [
|
||||
0.09941192804175551,
|
||||
0.016572084086884566
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.07597383189748476,
|
||||
0.016403469943482765
|
||||
],
|
||||
"adapter_eligible_seconds": 8.083969085000064,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.11170387210996009,
|
||||
0.016572084086884566
|
||||
]
|
||||
},
|
||||
"turn_speed_2_8": {
|
||||
"seconds": 2.9056519089999995,
|
||||
"core_c0_c1_rms": [
|
||||
2.286598281178436,
|
||||
0.3420570411209625
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
2.4371443775668484,
|
||||
0.37360610108051345
|
||||
],
|
||||
"adapter_eligible_seconds": 2.9056519089999995,
|
||||
"adapter_c0_c1_rms": [
|
||||
2.3669481787447944,
|
||||
0.3420570411209625
|
||||
]
|
||||
},
|
||||
"small_speed_8_15": {
|
||||
"seconds": 32.021085868,
|
||||
"core_c0_c1_rms": [
|
||||
0.028387860150467858,
|
||||
0.0067602044219821
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.036941169841952046,
|
||||
0.007349671742584858
|
||||
],
|
||||
"adapter_eligible_seconds": 32.021085868,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.033317964465740625,
|
||||
0.0067602044219821
|
||||
]
|
||||
},
|
||||
"turn_speed_8_15": {
|
||||
"seconds": 22.160645306000006,
|
||||
"core_c0_c1_rms": [
|
||||
0.28668316323581683,
|
||||
0.0671915746138579
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.3391022553367454,
|
||||
0.07297678510653233
|
||||
],
|
||||
"adapter_eligible_seconds": 22.160645306000006,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.32040564199313326,
|
||||
0.0671915746138579
|
||||
]
|
||||
},
|
||||
"small_speed_15_55": {
|
||||
"seconds": 120.82922132800007,
|
||||
"core_c0_c1_rms": [
|
||||
0.010802222799973712,
|
||||
0.004282512931613862
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.03987958989535526,
|
||||
0.0065793876512313535
|
||||
],
|
||||
"adapter_eligible_seconds": 120.82922132800007,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.016212091011218406,
|
||||
0.004282512931613862
|
||||
]
|
||||
},
|
||||
"turn_speed_15_55": {
|
||||
"seconds": 33.332073582999925,
|
||||
"core_c0_c1_rms": [
|
||||
0.07257996778378971,
|
||||
0.03628926246224237
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.3416081588451539,
|
||||
0.027322786376822172
|
||||
],
|
||||
"adapter_eligible_seconds": 33.332073582999925,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.08724830487921002,
|
||||
0.03628926246224237
|
||||
]
|
||||
},
|
||||
"pose_quiet": {
|
||||
"seconds": 145.24136911700015,
|
||||
"core_c0_c1_rms": [
|
||||
0.012566204405760934,
|
||||
0.004614194153565963
|
||||
],
|
||||
"recorded_v8_c0_c1_rms": [
|
||||
0.03855743841849719,
|
||||
0.006621274926345545
|
||||
],
|
||||
"adapter_eligible_seconds": 145.24136911700015,
|
||||
"adapter_c0_c1_rms": [
|
||||
0.01710948709543248,
|
||||
0.004614194153565963
|
||||
]
|
||||
}
|
||||
},
|
||||
"workspace_head": "01f8d51c82b3e863f1012d383b5994813ef01b81",
|
||||
"opendbc_import_head": "72a775d35e54c21ff5c5798acef22016eedcc0a7",
|
||||
"opendbc_import_path": "/Users/ibpersonal/.codex/worktrees/b926/sunnypilot/opendbc_repo",
|
||||
"source_sha256": {
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/model_action_replay.py": "7cddac7ce9e88cc32bc7afbec7f9df79b66bb4dfa2fa5f9b36654891fae23a19",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_path.py": "383538fc7cdae3bc28dffb71fe12ac5f3f9866ffbe6adfb7457f3593e9fc903a",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route95/route.npz": "6e5438867ea618e53ca395b60ff4b9b146256f1bf6f07dc8e92d663286074154",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route95/metadata.json": "f7c95a102e0e084bbcc9938399219080c1d3b26274b4ea3c3a9514ff7dc8ea8d",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route95/encoder_comparison.npz": "23bd05c4b6400299844acaba1d97051c96c23c682bf17b46e89e7f2cca5fce38",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route95/encoder_comparison.json": "1ab24a796d14817b8672ea6dfd39711d65cb06744eaaef7dcc02a840c392c4d3",
|
||||
"/Users/ibpersonal/.codex/worktrees/3548/sunnypilot/analysis/controller_search_20260904/route95/pose_candidate/pose_replay.npz": "9cfbb3c6f0b1fdb7e3d38e6b64b8c94cffbcc9fabe41f6344d84a9b657b6af9b"
|
||||
}
|
||||
},
|
||||
"route9b": {
|
||||
"scope": "Fixed-input command replay only; no physical improvement or stability claim.",
|
||||
"calibration_approved": false,
|
||||
"cycles": 82386,
|
||||
"eligible_cycles": 70703,
|
||||
"status_counts": {
|
||||
"inactive": 11683,
|
||||
"active": 70703
|
||||
},
|
||||
"c1_exactly_unchanged": true,
|
||||
"same_validity": true,
|
||||
"field_slew_zero_c2_c3_pass": true,
|
||||
"float32_can_round_trips": 164772,
|
||||
"cohorts": {
|
||||
"eligible": {
|
||||
"cycles": 70703,
|
||||
"seconds": 711.571703289,
|
||||
"changed_c0_cycles": 10475,
|
||||
"mean_absolute_c0_change_m": 0.00219777369954635,
|
||||
"max_absolute_c0_change_m": 0.13000000000000078,
|
||||
"increased_absolute_c0_cycles": 9822,
|
||||
"decreased_absolute_c0_cycles": 633,
|
||||
"driver_input_percent": 3.0297623863274845,
|
||||
"baseline_peak_absolute_c0_m": 3.96,
|
||||
"candidate_peak_absolute_c0_m": 3.99
|
||||
},
|
||||
"driver_clean": {
|
||||
"cycles": 63267,
|
||||
"seconds": 636.7469998759999,
|
||||
"changed_c0_cycles": 8901,
|
||||
"mean_absolute_c0_change_m": 0.0014713899370902674,
|
||||
"max_absolute_c0_change_m": 0.05999999999999961,
|
||||
"increased_absolute_c0_cycles": 8314,
|
||||
"decreased_absolute_c0_cycles": 568,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 2.24,
|
||||
"candidate_peak_absolute_c0_m": 2.24
|
||||
},
|
||||
"driver_clean_low_request_above_8mps": {
|
||||
"cycles": 40656,
|
||||
"seconds": 409.13067679800054,
|
||||
"changed_c0_cycles": 7132,
|
||||
"mean_absolute_c0_change_m": 0.0017587822497730527,
|
||||
"max_absolute_c0_change_m": 0.020000000000000462,
|
||||
"increased_absolute_c0_cycles": 6888,
|
||||
"decreased_absolute_c0_cycles": 244,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 0.11000000000000032,
|
||||
"candidate_peak_absolute_c0_m": 0.11000000000000032
|
||||
},
|
||||
"turn": {
|
||||
"cycles": 5459,
|
||||
"seconds": 54.939139939000256,
|
||||
"changed_c0_cycles": 119,
|
||||
"mean_absolute_c0_change_m": 0.00021719119817404867,
|
||||
"max_absolute_c0_change_m": 0.010000000000000675,
|
||||
"increased_absolute_c0_cycles": 119,
|
||||
"decreased_absolute_c0_cycles": 0,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 2.24,
|
||||
"candidate_peak_absolute_c0_m": 2.24
|
||||
},
|
||||
"right_entry": {
|
||||
"cycles": 298,
|
||||
"seconds": 2.9932484459999387,
|
||||
"changed_c0_cycles": 216,
|
||||
"mean_absolute_c0_change_m": 0.028050765542768245,
|
||||
"max_absolute_c0_change_m": 0.10000000000000009,
|
||||
"increased_absolute_c0_cycles": 216,
|
||||
"decreased_absolute_c0_cycles": 0,
|
||||
"driver_input_percent": 20.395198628296086,
|
||||
"baseline_peak_absolute_c0_m": 2.95,
|
||||
"candidate_peak_absolute_c0_m": 3.0300000000000002
|
||||
},
|
||||
"right_exit": {
|
||||
"cycles": 115,
|
||||
"seconds": 1.156770048999988,
|
||||
"changed_c0_cycles": 0,
|
||||
"mean_absolute_c0_change_m": 0.0,
|
||||
"max_absolute_c0_change_m": 0.0,
|
||||
"increased_absolute_c0_cycles": 0,
|
||||
"decreased_absolute_c0_cycles": 0,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 0.7199999999999998,
|
||||
"candidate_peak_absolute_c0_m": 0.7199999999999998
|
||||
}
|
||||
},
|
||||
"baseline_commands_vs_recorded_publications": {
|
||||
"paired_cycles": 70703,
|
||||
"within_one_quantum_cycles": 65890,
|
||||
"maximum_absolute_error_c0_c1": [
|
||||
0.25000004768371564,
|
||||
0.0005000143051147043
|
||||
]
|
||||
},
|
||||
"timing": "Publication-time proxy, causal carState, exact consumed model; full SubMaster health unavailable.",
|
||||
"baseline_version": "v3",
|
||||
"baseline_revision": "01f8d51c82b3e863f1012d383b5994813ef01b81",
|
||||
"candidate_version": "current",
|
||||
"candidate_revision": "working_tree",
|
||||
"focus_windows": [
|
||||
[
|
||||
"right_entry",
|
||||
"637",
|
||||
"640"
|
||||
],
|
||||
[
|
||||
"right_exit",
|
||||
"642.7",
|
||||
"643.852"
|
||||
]
|
||||
],
|
||||
"baseline_source_sha256": "5b9f7946f29b5fcd94ca93e53be14a12b18ae74d7ca27380e92b90c8c599dce0",
|
||||
"candidate_source_sha256": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21",
|
||||
"source_rlog_sha256": {
|
||||
"84865544361f55cb_0000009b--e4616dddaa--0--rlog.zst": "22746f7119109b73ed7f2c26ce8c99f87136e9124fb7fc14c9554409a28a7c3f",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--1--rlog.zst": "4c2e1d7083c31a2b37d0f8dd3be4d330898511b7e02c26f7d40ca9bc2779397d",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--2--rlog.zst": "62f3e049e220cd3681fadf386f2969537bd571998ae2f6ba2d08479428b5a28f",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--3--rlog.zst": "83bf0131b2d36b2ba7e5ba050bbc13c0a3350feb5c9b89dc9c87d3a37abebfb3",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--4--rlog.zst": "430985a80dd6e10f7abeb89457a17022e6bb6978617f415c905f584b1647603e",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--5--rlog.zst": "8c0c5ae6323ec33b3e14f84ca834f70cb56f6b29f471a350f1e3efc06b6ba553",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--6--rlog.zst": "db53dfa8156b9d66792c3eff0b2ce5d31b71ad41cc580dec85f528845593c184",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--7--rlog.zst": "91b0b3be10cb7d7d7f7dd2024d8f9ee99d1e9fd2204203a3a9a2f2f1c6e3fa03",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--8--rlog.zst": "687dbbfc49837efbfe8fa6bc091e40f7fad2908832234d7884f4616d1bc9ccff",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--9--rlog.zst": "a88ec4d25b04cdbf5844686fc77f6b28dca920c9b164e37ebf69844a3ae398fc",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--10--rlog.zst": "fe6b29580a6c94e1c236d13e18db4cd9f31cc1b25d52e1e6e19a5021125c9932",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--11--rlog.zst": "150d31b1944d7a1b8c562f3aee20b66cefa6c4e8d02660ec889907d835142f45",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--12--rlog.zst": "e105e5f703a70f36f1989c66fb46c35a65ff60a265b4332e10fa2e6875a2ced4",
|
||||
"84865544361f55cb_0000009b--e4616dddaa--13--rlog.zst": "69304cf0c81401374f047c5022ca47d257ff9e59e30ec171325a2ee1e7ed13d4"
|
||||
},
|
||||
"opendbc_head": "c21a9013700734dd20b09e05aa68329ad8cc20f9",
|
||||
"source_sha256": {
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/damping_replay.py": "8c4f1f987b65e2be146c473a6e529168f08b1f7ce3c9514e22a9874450fa430e",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21"
|
||||
}
|
||||
},
|
||||
"route9e": {
|
||||
"scope": "Fixed-input command replay only; no physical improvement or stability claim.",
|
||||
"calibration_approved": false,
|
||||
"cycles": 83668,
|
||||
"eligible_cycles": 74669,
|
||||
"status_counts": {
|
||||
"inactive": 8999,
|
||||
"active": 74669
|
||||
},
|
||||
"c1_exactly_unchanged": true,
|
||||
"same_validity": true,
|
||||
"field_slew_zero_c2_c3_pass": true,
|
||||
"float32_can_round_trips": 167336,
|
||||
"cohorts": {
|
||||
"eligible": {
|
||||
"cycles": 74669,
|
||||
"seconds": 751.5479497329998,
|
||||
"changed_c0_cycles": 8108,
|
||||
"mean_absolute_c0_change_m": 0.00147612892002983,
|
||||
"max_absolute_c0_change_m": 0.13000000000000034,
|
||||
"increased_absolute_c0_cycles": 7665,
|
||||
"decreased_absolute_c0_cycles": 423,
|
||||
"driver_input_percent": 2.7324748055386094,
|
||||
"baseline_peak_absolute_c0_m": 4.38,
|
||||
"candidate_peak_absolute_c0_m": 4.38
|
||||
},
|
||||
"driver_clean": {
|
||||
"cycles": 66321,
|
||||
"seconds": 667.5291971840106,
|
||||
"changed_c0_cycles": 7257,
|
||||
"mean_absolute_c0_change_m": 0.0012096637080993352,
|
||||
"max_absolute_c0_change_m": 0.08000000000000007,
|
||||
"increased_absolute_c0_cycles": 6883,
|
||||
"decreased_absolute_c0_cycles": 374,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 2.4699999999999998,
|
||||
"candidate_peak_absolute_c0_m": 2.4699999999999998
|
||||
},
|
||||
"driver_clean_low_request_above_8mps": {
|
||||
"cycles": 39635,
|
||||
"seconds": 398.9613925449994,
|
||||
"changed_c0_cycles": 5427,
|
||||
"mean_absolute_c0_change_m": 0.0013748276784907216,
|
||||
"max_absolute_c0_change_m": 0.020000000000000462,
|
||||
"increased_absolute_c0_cycles": 5341,
|
||||
"decreased_absolute_c0_cycles": 86,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 0.07000000000000028,
|
||||
"candidate_peak_absolute_c0_m": 0.07000000000000028
|
||||
},
|
||||
"turn": {
|
||||
"cycles": 6219,
|
||||
"seconds": 62.59491144700405,
|
||||
"changed_c0_cycles": 244,
|
||||
"mean_absolute_c0_change_m": 0.000391833417652352,
|
||||
"max_absolute_c0_change_m": 0.010000000000000675,
|
||||
"increased_absolute_c0_cycles": 244,
|
||||
"decreased_absolute_c0_cycles": 0,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 2.4699999999999998,
|
||||
"candidate_peak_absolute_c0_m": 2.4699999999999998
|
||||
},
|
||||
"left_entry": {
|
||||
"cycles": 238,
|
||||
"seconds": 2.4048367620016506,
|
||||
"changed_c0_cycles": 204,
|
||||
"mean_absolute_c0_change_m": 0.03167584138917884,
|
||||
"max_absolute_c0_change_m": 0.07000000000000028,
|
||||
"increased_absolute_c0_cycles": 204,
|
||||
"decreased_absolute_c0_cycles": 0,
|
||||
"driver_input_percent": 2.403816920672437,
|
||||
"baseline_peak_absolute_c0_m": 2.6100000000000003,
|
||||
"candidate_peak_absolute_c0_m": 2.6399999999999997
|
||||
},
|
||||
"left_peak": {
|
||||
"cycles": 288,
|
||||
"seconds": 2.891819394000777,
|
||||
"changed_c0_cycles": 6,
|
||||
"mean_absolute_c0_change_m": 0.00022373297976799967,
|
||||
"max_absolute_c0_change_m": 0.019999999999999574,
|
||||
"increased_absolute_c0_cycles": 6,
|
||||
"decreased_absolute_c0_cycles": 0,
|
||||
"driver_input_percent": 6.41034524444729,
|
||||
"baseline_peak_absolute_c0_m": 2.9400000000000004,
|
||||
"candidate_peak_absolute_c0_m": 2.95
|
||||
},
|
||||
"left_exit": {
|
||||
"cycles": 219,
|
||||
"seconds": 2.203320298998733,
|
||||
"changed_c0_cycles": 11,
|
||||
"mean_absolute_c0_change_m": 0.0008062973780191794,
|
||||
"max_absolute_c0_change_m": 0.019999999999999574,
|
||||
"increased_absolute_c0_cycles": 11,
|
||||
"decreased_absolute_c0_cycles": 0,
|
||||
"driver_input_percent": 9.936619705158035,
|
||||
"baseline_peak_absolute_c0_m": 2.51,
|
||||
"candidate_peak_absolute_c0_m": 2.51
|
||||
},
|
||||
"reversal": {
|
||||
"cycles": 554,
|
||||
"seconds": 5.570295188001182,
|
||||
"changed_c0_cycles": 54,
|
||||
"mean_absolute_c0_change_m": 0.002577813237060219,
|
||||
"max_absolute_c0_change_m": 0.04999999999999982,
|
||||
"increased_absolute_c0_cycles": 29,
|
||||
"decreased_absolute_c0_cycles": 25,
|
||||
"driver_input_percent": 0.0,
|
||||
"baseline_peak_absolute_c0_m": 0.21999999999999975,
|
||||
"candidate_peak_absolute_c0_m": 0.21999999999999975
|
||||
}
|
||||
},
|
||||
"baseline_commands_vs_recorded_publications": {
|
||||
"paired_cycles": 74669,
|
||||
"within_one_quantum_cycles": 69690,
|
||||
"maximum_absolute_error_c0_c1": [
|
||||
0.369999988079071,
|
||||
0.0010000016689301061
|
||||
]
|
||||
},
|
||||
"timing": "Publication-time proxy, causal carState, exact consumed model; full SubMaster health unavailable.",
|
||||
"baseline_version": "v3",
|
||||
"baseline_revision": "01f8d51c82b3e863f1012d383b5994813ef01b81",
|
||||
"candidate_version": "current",
|
||||
"candidate_revision": "working_tree",
|
||||
"focus_windows": [
|
||||
[
|
||||
"left_entry",
|
||||
"173",
|
||||
"175.4"
|
||||
],
|
||||
[
|
||||
"left_peak",
|
||||
"175.4",
|
||||
"178.3"
|
||||
],
|
||||
[
|
||||
"left_exit",
|
||||
"178.3",
|
||||
"180.5"
|
||||
],
|
||||
[
|
||||
"reversal",
|
||||
"728",
|
||||
"734"
|
||||
]
|
||||
],
|
||||
"baseline_source_sha256": "5b9f7946f29b5fcd94ca93e53be14a12b18ae74d7ca27380e92b90c8c599dce0",
|
||||
"candidate_source_sha256": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21",
|
||||
"source_rlog_sha256": {
|
||||
"84865544361f55cb_0000009e--592f7dc149--0--rlog.zst": "de63532ae6aedf5dc7fd3ac8e47a2d96a8f065614f4aca79ae2f120ea00390c7",
|
||||
"84865544361f55cb_0000009e--592f7dc149--1--rlog.zst": "67b58197158b3e8f0581643f6657d2a85c47b0bb75dd3e00306d9ffae790008b",
|
||||
"84865544361f55cb_0000009e--592f7dc149--2--rlog.zst": "a4840f338f51b5f1864c79ce3a4f2b11dc13d58d52b8459961c9da57237f2cc2",
|
||||
"84865544361f55cb_0000009e--592f7dc149--3--rlog.zst": "ff3ea8d948006ab19c4dbfeeff59a509de3f193da47b91c88faa27ecd0b3f1fc",
|
||||
"84865544361f55cb_0000009e--592f7dc149--4--rlog.zst": "5ff1996c2336299128a11e657c32bc21921716b7e3bd401de69a2d49484aa223",
|
||||
"84865544361f55cb_0000009e--592f7dc149--5--rlog.zst": "238763457e89896933afaf9a5df325469ee8c02ddc53550df252531bc94fc540",
|
||||
"84865544361f55cb_0000009e--592f7dc149--6--rlog.zst": "2ee86de80cfd762be10cd2dfb2895ddbee6b813706e9c7261460203e09b9bc4d",
|
||||
"84865544361f55cb_0000009e--592f7dc149--7--rlog.zst": "017801f080861c63d799f87aebe30ab57cdf82f078ac882e6187d3870c403538",
|
||||
"84865544361f55cb_0000009e--592f7dc149--8--rlog.zst": "3729016bd1f00bb1077613b63fe25b21ba7112822b994e0e0aa2b4cd93bdb940",
|
||||
"84865544361f55cb_0000009e--592f7dc149--9--rlog.zst": "c40b3c1f6eb9252b85f176fee32cae16c23eaad3db830f6bbf37a730034a5ccf",
|
||||
"84865544361f55cb_0000009e--592f7dc149--10--rlog.zst": "e0ce8f231798073e5bbc34551fbd9493f169a06a068adc1957ef8cd914b71fbb",
|
||||
"84865544361f55cb_0000009e--592f7dc149--11--rlog.zst": "90863353f7c862a05618b4d0761dda3fcedd3cf6b4234c21c6dcc2eee3ec20b2",
|
||||
"84865544361f55cb_0000009e--592f7dc149--12--rlog.zst": "e064f8abde9615b2daf00f469ef36b438cd4b3a1637069467c84de91bee3bdf1",
|
||||
"84865544361f55cb_0000009e--592f7dc149--13--rlog.zst": "790fc438ce2675e0690fdce3b07bf54fdf6d06b17bbe7f5ff447adfa71189754",
|
||||
"84865544361f55cb_0000009e--592f7dc149--14--rlog.zst": "b98f5a5be660013b1fe11250fdc24ee4fe1d9dd1301b787ddfdff1f2bcd3e27f"
|
||||
},
|
||||
"opendbc_head": "c21a9013700734dd20b09e05aa68329ad8cc20f9",
|
||||
"source_sha256": {
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/tools/ford_pscm_lab/damping_replay.py": "8c4f1f987b65e2be146c473a6e529168f08b1f7ce3c9514e22a9874450fa430e",
|
||||
"/Users/ibpersonal/.codex/worktrees/1a1c/sunnypilot/openpilot/selfdrive/controls/lib/ford_model_action.py": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ablation_comparison": {
|
||||
"route9b": {
|
||||
"lab_variant_commands_exactly_match_production": true,
|
||||
"opposed_nonzero_c0_commands": 24,
|
||||
"max_absolute_difference_on_opposed_commands_m": 0.05999999999999961
|
||||
},
|
||||
"route9e": {
|
||||
"lab_variant_commands_exactly_match_production": true,
|
||||
"opposed_nonzero_c0_commands": 30,
|
||||
"max_absolute_difference_on_opposed_commands_m": 0.02999999999999936
|
||||
}
|
||||
},
|
||||
"total_original_route_cycles": 299604,
|
||||
"total_float32_can_round_trips": 817346,
|
||||
"source_sha256": {
|
||||
"openpilot/selfdrive/controls/lib/ford_model_action.py": "7a226cf3cdf6dc8c3b15829078a7b93e486ba4722c4a1d93ceb55dd2e6c77b21",
|
||||
"openpilot/selfdrive/controls/tests/test_ford_model_action.py": "30cfffb86fcd3830320e9b2ec1f65cdfd219684a55d0bc0bb13f77966cc18b53",
|
||||
"openpilot/selfdrive/controls/tests/test_ford_model_action_adapter.py": "8ab5dd52958f13a2426e07f8713b7d8d56c52b8a05d173ffba87ffccc0087796",
|
||||
"openpilot/selfdrive/controls/tests/test_ford_model_action_damping.py": "a7062d2c3c46b3abcd4c3facd97970644ce609fb55285daa0359681950948250",
|
||||
"openpilot/selfdrive/controls/tests/test_ford_model_action_prediction.py": "eb50e1d6acdb5e7332fadc3dcfcfa1b3809545e607c47c54b0ba3de80b2df208",
|
||||
"openpilot/selfdrive/controls/tests/test_ford_model_action_selection.py": "f826c6328f0abac2a61f1a0a6f8d119fdbab858e363cb466d84ba9a7783059cd",
|
||||
"openpilot/selfdrive/controls/tests/test_ford_controlsd_logging.py": "175a1969ac83b14b88798bbde37371f740cd05cf3f8f6e5d21bbf9afe857bb57",
|
||||
"tools/ford_pscm_lab/__init__.py": "db4b8b7d2e317ed34ca0ec220bf9d53e7b80a23e766f4e1cb2c8224dada45f2e",
|
||||
"tools/ford_pscm_lab/damping_replay.py": "8c4f1f987b65e2be146c473a6e529168f08b1f7ce3c9514e22a9874450fa430e",
|
||||
"tools/ford_pscm_lab/model_action_replay.py": "7cddac7ce9e88cc32bc7afbec7f9df79b66bb4dfa2fa5f9b36654891fae23a19",
|
||||
"tools/ford_pscm_lab/stress_model_action.py": "66adef6cba120a1a3d8e9730ea987ef49f8fe283023362dbb99608dee0e3229f",
|
||||
"tools/ford_pscm_lab/test_model_action_replay.py": "1f7d90c06ec09b405571565632653dc7d984b106b955877916d4a0836c11e3b5",
|
||||
"openpilot/sunnypilot/sunnylink/settings_ui_src/pages/vehicle.yaml": "41ac31726b941e470f5be34e2523dc1fc2b25fb6a12705ab267a57ed4f3c27c8",
|
||||
"openpilot/sunnypilot/sunnylink/settings_ui.json": "c44c75e02042a0485b5bd7896e0b794761d768bbe513b6dc9c2c986ddd22b44c"
|
||||
},
|
||||
"artifact_sha256": {
|
||||
".cache/ford_prediction_uncapped/uncapped_red.txt": "09160dbc36320be886561cfdbdd21eef0c8bc420a11bae9bb1a8dd43f7b93a3c",
|
||||
".cache/ford_prediction_uncapped/suite_run.txt": "99b3bd2af256c9a4a083cb015a51a458a26ac02a8cad73c28c47470f9a5eb910",
|
||||
".cache/ford_prediction_uncapped/coverage.json": "50a282c90fa86147ed6925fdf37918471f8e5204a4c1e1ef9adb39335e220f42",
|
||||
".cache/ford_prediction_uncapped/stress.json": "e4226f403a3a914b9f3a7868b14a2b763215ec1f77e7d45ac06604859acc9fb6",
|
||||
".cache/ford_prediction_uncapped/route90/report.json": "c5cee3c56f9f7f292c8069be22dd1ffb1649055fccaf829b28d96bdec6558541",
|
||||
".cache/ford_prediction_uncapped/route95/report.json": "6fcc8a3bd7abb5b513888cb64426670e7e05f8b2100b1876be196a800cec2c90",
|
||||
".cache/ford_prediction_uncapped/final9b/report.json": "79faca3ff89e2015dad4823d4d988ea26ead55c7e51e4a594258102d32701b07",
|
||||
".cache/ford_prediction_uncapped/final9e/report.json": "9b003ce97225e65e03e351c5e133f3fed08dc79e026f73308098d51a98dd67ae",
|
||||
".cache/ford_prediction_uncapped/route90/commands.npz": "8787aabbe7007bcd7c68976c272085761aec6a4f360fa6cc8c8553bd1421c92b",
|
||||
".cache/ford_prediction_uncapped/route95/commands.npz": "349b716bd910cd030716b9ef519ee24f620fae5280776ffa871bc43318939400",
|
||||
".cache/ford_prediction_uncapped/final9b/commands.npz": "ff4a14fc74b144800f459c250be961ebb05a46a20e6ad1fc0394a659c9854394",
|
||||
".cache/ford_prediction_uncapped/final9e/commands.npz": "59f6046acb10b00ef00e7b9feb5b8547139a67a2f8767d7cbfe3603a59e6c77d"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
# Experimental Ford path prediction, v3
|
||||
|
||||
This document and its counts describe archived v3. The
|
||||
[current v4 experiment](ford_model_action_full_prediction.md) removes the extra
|
||||
15 cm / 25% prediction adjustment cap.
|
||||
|
||||
The latest driven route9e used v1 (`5fc16abc7`), before the v2 yaw damping.
|
||||
It often follows the requested steering angle closely, but some tight turns
|
||||
fall behind after a reasonable initial turn-in. The requested angle is replanned
|
||||
@@ -97,13 +101,15 @@ See `ford_model_action_prediction_validation.json` for provenance and counts.
|
||||
|
||||
## Reproduce and select
|
||||
|
||||
Use the native dependencies and suite command in the
|
||||
[drive-test guide](ford_model_action_drive_test.md). New-route comparisons use
|
||||
To reproduce the archived suite and stress results, use v3 commit
|
||||
`01f8d51c82b3e863f1012d383b5994813ef01b81` with the native dependencies and
|
||||
suite command in the [drive-test guide](ford_model_action_drive_test.md).
|
||||
Current replay tooling can select the immutable v3 source explicitly. New-route comparisons use
|
||||
the deployment opendbc pin `c21a9013700734dd20b09e05aa68329ad8cc20f9`:
|
||||
|
||||
```sh
|
||||
python -m tools.ford_pscm_lab.damping_replay /path/to/route9e/rlogs --baseline v2 --candidate current --window left_entry 173 175.4 --window left_peak 175.4 178.3 --window left_exit 178.3 180.5 --window reversal 728 734 --window final_entry 822 825.5 --output /path/to/separate/route9e-results
|
||||
python -m tools.ford_pscm_lab.damping_replay /path/to/route9b/rlogs --baseline v2 --candidate current --window right_entry 637 640 --window right_exit_before_strong_input 642.7 643.852 --output /path/to/separate/route9b-results
|
||||
python -m tools.ford_pscm_lab.damping_replay /path/to/route9e/rlogs --baseline v2 --candidate v3 --window left_entry 173 175.4 --window left_peak 175.4 178.3 --window left_exit 178.3 180.5 --window reversal 728 734 --window final_entry 822 825.5 --output /path/to/separate/route9e-results
|
||||
python -m tools.ford_pscm_lab.damping_replay /path/to/route9b/rlogs --baseline v2 --candidate v3 --window right_entry 637 640 --window right_exit_before_strong_input 642.7 643.852 --output /path/to/separate/route9b-results
|
||||
python -m tools.ford_pscm_lab.stress_model_action --cycles 200000 --seed 20260907 --opendbc-revision c21a9013700734dd20b09e05aa68329ad8cc20f9 --output /path/to/stress.json
|
||||
```
|
||||
|
||||
|
||||
@@ -17,16 +17,14 @@ HEADING_TIME_S = 1.0
|
||||
EXCESS_YAW_DEADBAND = .02 # rad/s; above the observed approximately .008 rad/s Ford yaw offset
|
||||
EXCESS_YAW_LOOKAHEAD_S = .2 # engineering choice, not an identified PSCM delay
|
||||
CALIBRATION_APPROVED = False
|
||||
PREDICTION_TIME_S = .15 # bounded geometric preview, not an identified actuator delay
|
||||
PREDICTION_LIMIT_M = .15
|
||||
PREDICTION_FRACTION = .25
|
||||
PREDICTION_TIME_S = .15 # geometric preview, not an identified actuator delay
|
||||
|
||||
|
||||
def _predict_offset(path, c0, desired_curvature, speed):
|
||||
"""Read the same path from a predicted pose along the selected curvature.
|
||||
|
||||
A matched constant-radius path retains its offset. Developing/flattening
|
||||
bends can move the target earlier, bounded to 15 cm and 25% of current C0.
|
||||
bends can move the target earlier. The core retains field limits and slew.
|
||||
Use only available geometry; shortened horizons taper prediction to zero.
|
||||
"""
|
||||
station, longitudinal, lateral, _ = path
|
||||
@@ -41,8 +39,7 @@ def _predict_offset(path, c0, desired_curvature, speed):
|
||||
predicted = math.cos(rotation)*y-math.sin(rotation)*x+translation
|
||||
if not _finite(predicted):
|
||||
return c0
|
||||
limit = min(PREDICTION_LIMIT_M, PREDICTION_FRACTION*abs(c0))
|
||||
return c0+float(np.clip(predicted-c0, -limit, limit))
|
||||
return predicted
|
||||
|
||||
|
||||
def _packed(value, resolution, offset):
|
||||
@@ -59,7 +56,7 @@ def _finite(*values):
|
||||
|
||||
|
||||
def encode_model_action(model, desired_curvature, speed):
|
||||
"""Encode bounded predicted y(7) and max(7, v*1s)*selected limited curvature.
|
||||
"""Encode predicted y(7) and max(7, v*1s)*selected limited 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.
|
||||
@@ -145,7 +142,7 @@ class FordModelActionController:
|
||||
def reset(self, status='inactive'):
|
||||
self.core.reset()
|
||||
self.last_time = self.last_measurement_time = self.last_model_time = None
|
||||
self.diagnostics = {'status': status, 'hypothesis': 'model-action-c0-c1-prediction-v3',
|
||||
self.diagnostics = {'status': status, 'hypothesis': 'model-action-c0-c1-prediction-v4',
|
||||
'calibration_approved': CALIBRATION_APPROVED, 'command': (0., 0., 0., 0.)}
|
||||
|
||||
def update(self, model, desired_curvature, *, yaw_rate, speed, now, measurement_time, model_time, reference_time,
|
||||
@@ -176,7 +173,7 @@ class FordModelActionController:
|
||||
self.reset('invalid_path')
|
||||
return command
|
||||
self.last_time, self.last_measurement_time, self.last_model_time = now, measurement_time, model_time
|
||||
self.diagnostics = {'status': 'active', 'hypothesis': 'model-action-c0-c1-prediction-v3',
|
||||
self.diagnostics = {'status': 'active', 'hypothesis': 'model-action-c0-c1-prediction-v4',
|
||||
'calibration_approved': CALIBRATION_APPROVED, 'desired_curvature': desired_curvature,
|
||||
'yaw_rate': yaw_rate,
|
||||
'model_age': now - model_time, 'measurement_age': now - measurement_time, 'reference_age': now - reference_time,
|
||||
|
||||
@@ -53,7 +53,7 @@ class TestFordControlsLogging(unittest.TestCase):
|
||||
controls = SimpleNamespace(ford_path_controller=controller, desired_curvature=.005, curvature=.0025,
|
||||
sm=SimpleNamespace(logMonoTime={'modelV2': 123456789, 'carState': 123450000}))
|
||||
record = self.emit_controls_event('Ford C2-free path tracking', controls)
|
||||
self.assertEqual(record['hypothesis'], 'model-action-c0-c1-prediction-v3')
|
||||
self.assertEqual(record['hypothesis'], 'model-action-c0-c1-prediction-v4')
|
||||
self.assertIs(record['calibration_approved'], False)
|
||||
self.assertEqual(record['command'][2:], [0., 0.])
|
||||
self.assertEqual(record['status'], controller.diagnostics['status'])
|
||||
|
||||
@@ -54,19 +54,24 @@ def test_two_actuator_positions_are_sufficient_for_every_next_output():
|
||||
assert controller.update(model, desired, **kwargs) == copied.update(model, desired, **kwargs)
|
||||
|
||||
|
||||
def test_held_turn_releases_without_a_bias_tail_or_sign_reversal():
|
||||
def test_held_turn_releases_with_geometric_countersteering_and_no_retained_bias():
|
||||
for sign in (-1., 1.):
|
||||
controller = ModelActionController()
|
||||
for _ in range(400):
|
||||
out = controller.update(circle(sign*.01), sign*.01, speed=20., dt=.01)
|
||||
assert out.path_angle == pytest.approx(sign*.2)
|
||||
previous = np.array([out.path_offset, out.path_angle])
|
||||
previous = np.array([controller.c0, controller.c1])
|
||||
opposed = False
|
||||
for desired in sign*np.linspace(.01, 0., 101):
|
||||
out = controller.update(straight(), desired, speed=20., dt=.01)
|
||||
values = np.array([out.path_offset, out.path_angle])
|
||||
assert (abs(values) <= abs(previous)+1e-8).all()
|
||||
assert (sign*values >= -1e-8).all()
|
||||
rotation = desired*3.
|
||||
predicted = (1.-math.cos(rotation))/desired-10.*math.sin(rotation) if desired else 0.
|
||||
expected = previous+np.clip([predicted, 20.*desired]-previous, [-.04, -.005], [.04, .005])
|
||||
values = np.array([controller.c0, controller.c1])
|
||||
np.testing.assert_allclose(values, expected, atol=1e-10)
|
||||
opposed |= sign*out.path_offset < 0.
|
||||
previous = values
|
||||
assert opposed
|
||||
assert out == FordPath(True, 0., 0., 0., 0.)
|
||||
|
||||
|
||||
@@ -180,8 +185,9 @@ def test_arc_station_not_forward_x_or_model_heading_determines_offset():
|
||||
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, so base y(7)=4.6. Prediction reaches its +.15m bound.
|
||||
assert target.path_offset == pytest.approx(4.75)
|
||||
# Arc length is 1.25*x. At station 10, x=8 and y=6.4; use the full predicted pose.
|
||||
expected = math.cos(-.03)*6.4-math.sin(-.03)*8.+(1.-math.cos(-.03))/-.01
|
||||
assert target.path_offset == pytest.approx(expected)
|
||||
assert target.path_angle == pytest.approx(-.2)
|
||||
|
||||
|
||||
|
||||
@@ -81,7 +81,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(.3) # Preview accounts for selected turning toward the offset path.
|
||||
assert result.path_offset == pytest.approx(.14) # Full preview accounts for selected turning toward the offset path.
|
||||
assert result.path_angle == pytest.approx(.05)
|
||||
broken = straight(.4)
|
||||
broken.position.y[5] = math.nan
|
||||
@@ -115,7 +115,7 @@ def test_release_keeps_current_geometry_and_may_grow_c0_while_c1_decreases():
|
||||
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 abs(released.path_offset) > abs(after.path_offset) # Less expected turning raises the future-frame offset.
|
||||
assert released.path_angle == pytest.approx(0.)
|
||||
|
||||
|
||||
|
||||
@@ -14,13 +14,15 @@ def geometric_offset(model):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sign', [-1., 1.])
|
||||
def test_developing_bend_advances_offset_with_bounded_request(sign):
|
||||
def test_developing_bend_uses_full_prediction_beyond_former_cap(sign):
|
||||
x = np.linspace(0., 30., 3001)
|
||||
model = make_model(x, sign*.001*x**3, np.zeros_like(x))
|
||||
base = geometric_offset(model)
|
||||
target = encode_model_action(model, 0., 10.)
|
||||
assert sign*target.path_offset > sign*base+.05
|
||||
assert target.path_offset == pytest.approx(base*1.25)
|
||||
station = np.r_[0., np.cumsum(np.hypot(np.diff(x), np.diff(model.position.y)))]
|
||||
assert target.path_offset == pytest.approx(np.interp(8.5, station, model.position.y))
|
||||
assert abs(target.path_offset-base) > .25*abs(base)
|
||||
assert target.path_angle == target.curvature == target.curvature_rate == 0.
|
||||
|
||||
|
||||
@@ -53,11 +55,23 @@ def test_straight_centering_and_near_zero_curvature_remain_well_conditioned(offs
|
||||
|
||||
|
||||
@pytest.mark.parametrize('offset', [-4., -.4, -.001, 0., .001, .4, 4.])
|
||||
def test_prediction_cannot_reverse_or_swamp_existing_centering(offset):
|
||||
for curvature in (-1., -.1, .1, 1.):
|
||||
value = encode_model_action(straight(offset), curvature, 55.).path_offset
|
||||
assert abs(value-offset) <= min(.15, .25*abs(offset))+1e-12
|
||||
assert value*offset >= 0.
|
||||
def test_full_rotated_path_prediction_is_independent_of_base_offset_magnitude(offset):
|
||||
station = np.linspace(0., 30., 301)
|
||||
for heading in (-.3, .3):
|
||||
model = make_model(station*np.cos(heading), offset+station*np.sin(heading), np.full_like(station, heading))
|
||||
value = encode_model_action(model, 0., 20.).path_offset
|
||||
assert value == pytest.approx(offset+10.*math.sin(heading))
|
||||
assert abs(value-geometric_offset(model)) > .15
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sign', [-1., 1.])
|
||||
def test_zero_near_offset_can_request_opposing_centering_from_the_predicted_pose(sign):
|
||||
curvature = sign*.01
|
||||
target = encode_model_action(straight(), curvature, 20.)
|
||||
expected = (1.-math.cos(curvature*3.))/curvature-10.*math.sin(curvature*3.)
|
||||
assert target.path_offset == pytest.approx(expected)
|
||||
assert sign*target.path_offset < -.25
|
||||
assert target.path_angle == pytest.approx(sign*.2)
|
||||
|
||||
|
||||
def test_short_horizon_holds_endpoint_and_available_prediction_tapers_to_zero():
|
||||
|
||||
@@ -2184,7 +2184,7 @@
|
||||
"needs_onroad_cycle": true,
|
||||
"title": "Selected-Action Path Tracking (Experimental)",
|
||||
"description": "Follow the selected steering plan with nearby model-path centering on the Ford CAN FD F-150 Lightning.",
|
||||
"details": "Uses a small, bounded prediction of the nearby model path to respond as bends develop, plus a heading request based on selected planned curvature. Reduces same-direction offset demand when measured turning exceeds the requested turn. Default off; this revised turn-entry and exit behavior is not road-validated. Enable only for controlled testing. On the Ford CAN FD F-150 Lightning this takes priority over PSCM Coefficient Observer; other vehicles retain their existing controller. Turning it off restores PSCM Coefficient Observer if selected, otherwise the original Ford path controller. Changes apply after a real offroad-to-onroad cycle, not immediately or on disengagement alone.",
|
||||
"details": "Uses a short prediction of the nearby model path to respond as bends develop, plus a heading request based on selected planned curvature. The predicted offset uses the full geometric request within the existing command limits and rate limits. Reduces same-direction offset demand when measured turning exceeds the requested turn. Default off; this revised turn-entry and exit behavior is not road-validated. Enable only for controlled testing. On the Ford CAN FD F-150 Lightning this takes priority over PSCM Coefficient Observer; other vehicles retain their existing controller. Turning it off restores PSCM Coefficient Observer if selected, otherwise the original Ford path controller. Changes apply after a real offroad-to-onroad cycle, not immediately or on disengagement alone.",
|
||||
"enablement": [
|
||||
{
|
||||
"type": "offroad_only"
|
||||
|
||||
@@ -15,7 +15,7 @@ sections:
|
||||
needs_onroad_cycle: true
|
||||
title: Selected-Action Path Tracking (Experimental)
|
||||
description: Follow the selected steering plan with nearby model-path centering on the Ford CAN FD F-150 Lightning.
|
||||
details: Uses a small, bounded prediction of the nearby model path to respond as bends develop, plus a heading request based on selected planned curvature. Reduces same-direction offset demand when measured turning exceeds the requested turn. Default off; this revised turn-entry and exit behavior is not road-validated. Enable only for controlled testing. On the Ford CAN FD F-150 Lightning this takes priority over PSCM Coefficient Observer; other vehicles retain their existing controller. Turning it off restores PSCM Coefficient Observer if selected, otherwise the original Ford path controller. Changes apply after a real offroad-to-onroad cycle, not immediately or on disengagement alone.
|
||||
details: Uses a short prediction of the nearby model path to respond as bends develop, plus a heading request based on selected planned curvature. The predicted offset uses the full geometric request within the existing command limits and rate limits. Reduces same-direction offset demand when measured turning exceeds the requested turn. Default off; this revised turn-entry and exit behavior is not road-validated. Enable only for controlled testing. On the Ford CAN FD F-150 Lightning this takes priority over PSCM Coefficient Observer; other vehicles retain their existing controller. Turning it off restores PSCM Coefficient Observer if selected, otherwise the original Ford path controller. Changes apply after a real offroad-to-onroad cycle, not immediately or on disengagement alone.
|
||||
enablement:
|
||||
- $ref: '#/macros/offroad'
|
||||
- key: FordPscmObserver
|
||||
|
||||
@@ -16,7 +16,7 @@ import zstandard
|
||||
|
||||
from openpilot.cereal import log
|
||||
from openpilot.selfdrive.controls.lib import ford_model_action
|
||||
from tools.ford_pscm_lab.model_action_replay import V1_REVISION, V2_REVISION, WireCheck, field_checks, load_controller, sample, verify_dependency
|
||||
from tools.ford_pscm_lab.model_action_replay import V1_REVISION, V2_REVISION, V3_REVISION, WireCheck, field_checks, load_controller, sample, verify_dependency
|
||||
|
||||
|
||||
DEPLOYMENT_OPENDBC = 'c21a9013700734dd20b09e05aa68329ad8cc20f9'
|
||||
@@ -73,7 +73,7 @@ def run(directory, output, baseline_version='v1', candidate_version='v2', window
|
||||
if output == directory or directory in output.parents:
|
||||
raise ValueError('Output must be outside the source route directory')
|
||||
verify_dependency(DEPLOYMENT_OPENDBC)
|
||||
revisions = {'v1': V1_REVISION, 'v2': V2_REVISION}
|
||||
revisions = {'v1': V1_REVISION, 'v2': V2_REVISION, 'v3': V3_REVISION}
|
||||
baseline_source = load_controller(revisions[baseline_version])
|
||||
candidate_source = ford_model_action if candidate_version == 'current' else load_controller(revisions[candidate_version])
|
||||
streams, models, sources, t0 = extract(directory)
|
||||
@@ -162,8 +162,8 @@ if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('rlog_directory', type=Path)
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
parser.add_argument('--baseline', choices=['v1', 'v2'], default='v1')
|
||||
parser.add_argument('--candidate', choices=['v2', 'current'], default='v2')
|
||||
parser.add_argument('--baseline', choices=['v1', 'v2', 'v3'], default='v1')
|
||||
parser.add_argument('--candidate', choices=['v2', 'v3', 'current'], default='v2')
|
||||
parser.add_argument('--window', action='append', nargs=3, metavar=('LABEL', 'START_SECONDS', 'END_SECONDS'), default=[])
|
||||
args = parser.parse_args()
|
||||
run(args.rlog_directory, args.output, args.baseline, args.candidate, args.window)
|
||||
|
||||
@@ -28,9 +28,10 @@ from openpilot.selfdrive.controls.lib.ford_path import _model_path
|
||||
PINNED_OPENDBC = '72a775d35e54c21ff5c5798acef22016eedcc0a7'
|
||||
V1_REVISION = '5fc16abc7662020706e29f57d31a6d5e2bc1293a'
|
||||
V2_REVISION = '744a97d9bc08d8743b250eceff7c88585b5480de'
|
||||
V3_REVISION = '01f8d51c82b3e863f1012d383b5994813ef01b81'
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
@lru_cache(maxsize=3)
|
||||
def load_controller(commit):
|
||||
"""Load exact archived Python source for offline comparisons, never production."""
|
||||
if len(commit) != 40 or any(c not in '0123456789abcdef' for c in commit):
|
||||
|
||||
@@ -71,7 +71,6 @@ def run(cycles, seed, output, opendbc_revision=PINNED_OPENDBC):
|
||||
assert out.valid == other.valid == expected_valid
|
||||
previous = np.array([c0, c1])
|
||||
if expected_valid:
|
||||
base = offset+7.*math.sin(heading)
|
||||
distance, rotation = speed*.15, desired*speed*.15
|
||||
# Independent full pose transform, including stable small-angle series.
|
||||
if abs(rotation) < 1e-5:
|
||||
@@ -81,10 +80,7 @@ def run(cycles, seed, output, opendbc_revision=PINNED_OPENDBC):
|
||||
ego_x, ego_y = math.sin(rotation)/desired, (1.-math.cos(rotation))/desired
|
||||
future_x, future_y = (7.+distance)*math.cos(heading), offset+(7.+distance)*math.sin(heading)
|
||||
predicted = math.cos(rotation)*(future_y-ego_y)-math.sin(rotation)*(future_x-ego_x)
|
||||
bound = min(.15, .25*abs(base))
|
||||
advanced = base+max(-bound, min(bound, predicted-base))
|
||||
assert abs(advanced-base) <= bound+1e-12 and advanced*base >= 0.
|
||||
target = (max(-5.11, min(5.11, advanced)), max(-.5, min(.5, max(7., speed)*desired)))
|
||||
target = (max(-5.11, min(5.11, predicted)), max(-.5, min(.5, max(7., speed)*desired)))
|
||||
# Independent piecewise scalar oracle; do not call the production helper.
|
||||
offset_target = target[0]
|
||||
if offset_target > 0. and yaw > 0.:
|
||||
@@ -127,7 +123,7 @@ def run(cycles, seed, output, opendbc_revision=PINNED_OPENDBC):
|
||||
'invalid_or_inactive_resets': resets, 'field_boundary_cases': boundary_cases,
|
||||
'float32_can_round_trips': wire.count, 'analytic_targets_scalar_slew_and_mirror_checks_pass': True,
|
||||
'bounded_excess_yaw_damping_checked': True,
|
||||
'bounded_geometric_prediction_checked': True,
|
||||
'full_geometric_prediction_checked': True,
|
||||
'direct_raw_float32_packing_matches_host_output': True, 'max_continuous_step_c0_c1': max_continuous_step.tolist(),
|
||||
'calibration_approved': False, 'scope': 'Numerical construction only; no PSCM response or closed-loop performance claims.',
|
||||
'opendbc_import_head': revision(dependency),
|
||||
|
||||
@@ -45,13 +45,13 @@ def test_archived_loader_uses_exact_source_and_records_its_hash(monkeypatch):
|
||||
monkeypatch.setattr(replay.subprocess, 'check_output', read_source)
|
||||
replay.load_controller.cache_clear()
|
||||
try:
|
||||
for commit in (replay.V1_REVISION, replay.V2_REVISION):
|
||||
for commit in (replay.V1_REVISION, replay.V2_REVISION, replay.V3_REVISION):
|
||||
module = replay.load_controller(commit)
|
||||
assert module.archived_value == 42
|
||||
assert module.source_sha256 == hashlib.sha256(source).hexdigest()
|
||||
assert calls[-1][-2:] == ['show', f'{commit}:openpilot/selfdrive/controls/lib/ford_model_action.py']
|
||||
assert replay.load_controller(commit) is module
|
||||
assert len(calls) == 2
|
||||
assert len(calls) == 3
|
||||
finally:
|
||||
replay.load_controller.cache_clear()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user