Ford angle-mode lateral: anti-weave smoothing of the command path - #171
Open
ghbarker wants to merge 2 commits into
Open
Ford angle-mode lateral: anti-weave smoothing of the command path#171ghbarker wants to merge 2 commits into
ghbarker wants to merge 2 commits into
Conversation
…ctors A pure estimator (angle_autocal.py) fits the two gain anchors from clean cornering evidence — steady curve segments and matched command/measured apexes — and nudges the applied factors toward the fit in small damped steps, verifying each step against fresh evidence before the next. Evidence is only taken while the loop is quietly tracking (hunting can never read as gain) and while the actuation delay is estimated, aligned against the command from lateral_delay ago so a moving command on winding roads still counts. Lifecycle (arm/disarm, persistence, nudge writes, lock) lives in a controller; the same estimator runs offline against logged drives. The sub-knee small-signal lift rides the FordHighSpeedDampening_ang knob merged in BluePilotDev#166 (mechanically the same multiplier); no duplicate param is added. Settings are on the on-device menus (comma 3X + comma four) and sunnylink. Menu neutral values leave steering bit-identical to today.
Five input-side filters (angle_smoothing.py) that damp the rhythmic straight-road weave: an asymmetric filter on |kappa| feeding the gain schedule (the primary fix), a low-pass on the model prediction, a slew on the exit blend, hysteresis on the curve-entering boolean, and a 1-LSB wire hold on the outgoing path_angle. A strength dial where 1.0 is bit-identical stock; no output-side lag by construction. Held wire frames are zero-ROC and cannot trip the panda path_angle checks. Toggle + strength on both device menus and sunnylink. Stacked on the auto-calibration PR (shares the lateral_angle_ext wiring).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The evidence that motivated this
00000006--31********) put numbers on it: a 0.23 Hz dither at ±0.033 m, a slow ±0.46 m lane sway, and the near-zero command crossing a wire code boundary 962 times/min (LatCtlPath_An_Actl's LSB is 0.0005 rad) — a square wave the PSCM integrates into felt motion.Why the fix is shaped this way
Input-side only, on principle and on data. A curvature-scheduled output low-pass was in the original package and was removed by measurement: a closed-loop rig (real strategy math + wire quantization + PSCM lag + delayed lane policy) showed it degrading station-keeping 2.5x — in-loop lag fights the model's own correction. Open-loop replay structurally cannot show this in either direction, which is why the rig exists.
Five elements, one pure module (
angle_smoothing.AngleSmoother, no I/O — unit-testable anywhere, same pattern as the auto-cal estimator): an asymmetric filter on the gain-schedule input (fast fixed attack, slow strength-scaled release — the primary fix), a low-pass on the model prediction (inside the variable-lookup slack, so free), a slew on the exit-blend ratio (boolean chatter becomes bounded ramps, not 4x steps), hysteresis on the curve-entering decision, and a 1-LSB hold on the outgoing wire value (a held frame is zero-ROC; the release step is far inside the tightest soft ROC — panda cannot be tripped).The dial has factor semantics, and 1.0 is provably stock. Menu 1.0 = bit-identical to the feature not existing (fuzz-tested for equality, not closeness); 2.0 = the log-tuned setting; 2.5 = max. The toggle ships ON at strength 1.0 — stock behavior until the driver steps up. Damping is always an explicit choice.
Curve entry is strength-independent by design: the attack RC is fixed, tested to reach 80% of a gain step in 0.2 s at every strength.
No stale state, ever: every element resets across disengage/human-turn/stall-blip discontinuities, and re-seeds from the live value when inactive — toggling on or stepping strength mid-drive picks up from the current steering state (both prior transient paths are regression-tested).
Distinct from the Add High Speed Low Curve Tuning Option #166 dampening knob, and compounding with it. The knob fixes amplitude (the car under-delivers tiny commands ~75–85%; a static gain restores them). This PR fixes modulation and dither: the gain-schedule band sits inside straight-road noise, so unfiltered |kappa| lets the weave modulate its own loop gain every cycle — a static value cannot fix a gain that flickers. And setting the knob above 1.0 (what the measured under-delivery calls for) amplifies the surviving dither proportionally — the boost makes this filtering more relevant, not less.
The evidence afterwards
MIN_KAPPAfloor sits at the gain-interp knee (coupling now documented at both sites), the wire hold's worst bias is two orders under the calibrator's stderr threshold, and the same hour-long drive that ran smoothing at 1.8 also locked the auto-calibration — the two features coexist measurably.What each change is
opendbc/sunnypilot/car/ford/angle_smoothing.py: the pureAngleSmoother— five elements, constants, strength semantics, transition-safe seeding.opendbc/sunnypilot/car/ford/lateral_angle_ext.py: one method call per element at the exact command-path point where it lives;smoothing_enabled/smoothing_strengthcompat properties for offline tooling.common/params_keys.h: registerFordAngleSmoothing(BOOL, default on) andFordAngleSmoothStrength(FLOAT, default 1.0 = stock).selfdrive/ui/bp/layouts/settings/bluepilot.py+mici/.../lateral_mici.py: toggle + strength stepper on both surfaces, angle-mode gated.sunnypilot/sunnylink/settings_ui.json: the two entries, same gating.opendbc/sunnypilot/car/ford/tests/test_angle_smoothing.py: the 13 pure unit tests.opendbc/sunnypilot/car/ford/tests/test_lateral_angle_ext.py: closed-loop strategy tests (OFF path equals unsmoothed math; filters reset on override paths; ROC properties hold).docs/ford-angle-smoothing.md: driver-facing guide — the numbers above, how to find a setting, hard guarantees, troubleshooting.Test it yourself
docs/ford-angle-smoothing.md.test_menu_one_is_bit_identical_passthroughholds it.python bp/angle_weave_analyze.py <dir> <route>(bp-tools) prints the band powers and sway amplitudes above.Risks / limits