docs: align path smoothing design and execution plan

This commit is contained in:
梁薄云
2026-07-29 12:07:18 +08:00
parent 8a782e934b
commit fc9aff4d84
2 changed files with 206 additions and 38 deletions
@@ -14,13 +14,18 @@
- Preserve start, goal, direction-segment order, gear-switch count, and gear-switch poses exactly.
- Never differentiate, resample, or fit across a gear-switch duplicate pair.
- Default output spacing is `0.05 m`; default swept-collision step is `0.025 m`.
- Default clearance reserve is `0.02 m`; default smoothing strength is `1.00`.
- A formal result publishes a path only for `Success` or explicitly verified `FallbackToCoarsePath`.
- Movement-bound rejection is retryable; invalid input, singular coefficients, and non-finite geometry are terminal failures.
- Every algorithm uses a read-only snapshot of its strong-typed options and maps evaluated points to the original direction segment by local arc length, never by raw point index.
- Rejected candidates may appear only in comparison diagnostics, never in `PathSmoothingResult.Path`.
- No speed, acceleration, time, SQP, Frenet, chassis, sensor, localization, or UI dependencies.
- Figure size is `7.16 × 5.2 in`; PNG size is `2148 × 1560 px` with 300 dpi metadata.
- Figure size is `7.16 × 5.2 in`; PNG size is `4296 × 3120 px` with 600 dpi metadata.
- Chinese text uses `SimSun`; English, numbers, Greek, and mathematics use `Times New Roman`.
- Fixed method colors are raw `#4D4D4D`, B-spline `#0072B2`, Bézier `#D55E00`, quintic `#009E73`, and curvature limits `#CC79A7`.
- `System.Drawing.Common` is a Windows-only report-rendering dependency; smoothing, validation, comparison, SVG, and CSV remain independent of its runtime availability.
- Text SVG is the editable master, not a directly submittable IEEE artifact; submission conversion to font-embedded or outlined PDF/EPS is an explicit external publishing step.
- Offline timing uses one warm-up and five measured deterministic runs per scenario and method; ranking uses the measured median only.
- Follow TDD for every task: failing verification first, minimal implementation second, full relevant verification before commit.
---
@@ -460,7 +465,124 @@ git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/CubicBSplineS
git commit -m "feat: add cubic b-spline path smoother"
```
### Task 6: Local cubic Bézier smoother
### Task 6: Align retryable feasibility, option snapshots, and arc-length references
**Context:** Tasks 15 are already committed. This corrective task resolves the review-discovered contract gaps before adding the remaining algorithms.
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingOptionsSnapshot.cs`
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PathReferenceInterpolator.cs`
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingCandidate.cs`
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmInput.cs`
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmRunner.cs`
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/CubicBSplineSmoother.cs`
- Test: `ClumsyPilot/tests/verify_path_smoothing_runner.ps1`
- Test: `ClumsyPilot/tests/verify_path_smoothing_bspline.ps1`
- Test: `ClumsyPilot/tests/verify_path_smoothing_algorithm_input.ps1`
**Interfaces:**
- Produces: `SmoothingCandidateStatus.Success`, `RetryableInfeasible`, or `Failed`.
- Produces: immutable method-specific option snapshots carried by `SmoothingAlgorithmInput`.
- Produces: `PathReferenceInterpolator.TryInterpolateByArcLength(IReadOnlyList<SmoothingPoint2D> points, double targetArcLength, out SmoothingPoint2D reference, out string reason)` for all three algorithms.
- [ ] **Step 1: Write failing status, snapshot, and reference tests**
The reflection tests must prove:
```text
RetryableInfeasible attempts exactly 1.00, 0.75, 0.50, 0.25 and ends Infeasible
Failed attempts exactly once and ends Failed
request/config mutation after construction cannot change the internal option snapshot
custom EndpointTangentScale changes the B-spline endpoint handle
non-uniform source samples interpolate by local ArcLength, not point-index ratio
non-finite/non-positive option scalars and a threshold outside (0, π] are rejected before retry
```
Use a non-uniform source with local arc lengths `0.00, 0.05, 0.10, 0.125`; at target arc `0.1125`, the reference must lie halfway through the final interval regardless of point count.
- [ ] **Step 2: Run and confirm RED**
```powershell
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_algorithm_input.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bspline.ps1
```
Expected: missing candidate status/snapshot/interpolator assertions fail, and B-spline still ignores the configured endpoint scale.
- [ ] **Step 3: Implement the retryable candidate state**
Use the exact internal states:
```csharp
internal enum SmoothingCandidateStatus
{
Success,
RetryableInfeasible,
Failed,
}
```
`SmoothingCandidate.Success(...)` requires complete segments. `RetryableInfeasible(reason)` and `Failed(reason)` carry no executable geometry. The runner continues only for `RetryableInfeasible`; it stops immediately for `Failed`. Exhausting retryable outcomes returns `AlgorithmRunResult.Infeasible(...)` even when no rejected comparison geometry exists.
- [ ] **Step 4: Implement immutable options and arc-length interpolation**
`SmoothingOptionsSnapshot` copies these six scalars from the request configuration into get-only values:
```text
CubicBSplineEndpointTangentScale
BezierCornerHeadingThresholdRadians
BezierMaximumWindowLengthMeters
BezierHandleLengthRatio
QuinticKnotSpacingMeters
QuinticMinimumKnotSpacingMeters
```
Snapshot construction rejects non-finite values; all scales, ratios, windows, and knot lengths must be positive, the Bézier threshold must lie in `(0, π]`, and quintic knot spacing must be at least its configured minimum. These are terminal input failures, not retryable geometry outcomes.
Use this construction boundary:
```csharp
internal SmoothingOptionsSnapshot(PathSmoothingConfiguration configuration);
internal SmoothingAlgorithmInput(
PreparedPath originalPath,
PlanningGridMap map,
VehicleParameters vehicle,
double maximumCollisionCheckStepMeters,
double minimumClearanceReserveMeters,
SmoothingOptionsSnapshot options);
```
`SmoothingAlgorithmInput.Options` is get-only and never exposes the mutable public configuration objects.
`PathReferenceInterpolator.TryInterpolateByArcLength` locates the bracketing source samples by `SmoothingPoint2D.ArcLength` and linearly interpolates position, heading, unwrapped heading, and clearance. A normalized full-segment parameter maps to `targetArc = u * segment.Points[last].ArcLength`; local algorithms map their window or knot interval directly to its endpoint arc lengths.
- [ ] **Step 5: Correct B-spline option and rejection semantics**
Replace the hard-coded endpoint scale with `input.Options.CubicBSplineEndpointTangentScale`. Replace point-index reference interpolation with `PathReferenceInterpolator`. Evaluated-point movement excess returns `RetryableInfeasible`; invalid values, impossible endpoint-tangent construction, and non-finite controls remain `Failed`. Never clamp evaluated curve samples.
- [ ] **Step 6: Run corrective and shared regression checks**
```powershell
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_algorithm_input.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bspline.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_geometry.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1
```
Expected: all pass; retryable geometry rejection uses all four strengths, while numerical failure still uses one.
- [ ] **Step 7: Commit**
```powershell
git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingOptionsSnapshot.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PathReferenceInterpolator.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingCandidate.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmInput.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmRunner.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/CubicBSplineSmoother.cs ClumsyPilot/tests/verify_path_smoothing_algorithm_input.ps1 ClumsyPilot/tests/verify_path_smoothing_runner.ps1 ClumsyPilot/tests/verify_path_smoothing_bspline.ps1
git commit -m "fix: align smoothing feasibility and option flow"
```
### Task 7: Local cubic Bézier smoother
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/LocalCubicBezierSmoother.cs`
@@ -468,7 +590,7 @@ git commit -m "feat: add cubic b-spline path smoother"
**Interfaces:**
- Implements: `IPathSmoother.Method == SmoothingMethod.LocalCubicBezier`.
- Consumes: heading-change threshold, maximum local window, handle-length ratio.
- Consumes: the immutable Bézier heading-change threshold, maximum local window, and handle-length ratio from `SmoothingAlgorithmInput.Options`.
- [ ] **Step 1: Write failing local-behavior tests**
@@ -496,7 +618,9 @@ SmoothingPoint2D value =
t * t * t * p3;
```
For each evaluated point, compare its displacement from the parameter-matched interpolated original reference with `max(0, reference.BodyClearance - MinimumClearanceReserveMeters)`. If any point exceeds that radius, return a failed candidate with no geometry; do not pointwise clamp or project curve samples. Retain original samples outside merged windows.
For each evaluated point, map `t` to `s_ref = s_entry + t * (s_exit - s_entry)` and obtain the source reference through `PathReferenceInterpolator`. Compare displacement with `max(0, reference.BodyClearance - MinimumClearanceReserveMeters)`. If any point exceeds that radius, return `RetryableInfeasible` with no executable geometry; do not pointwise clamp or project curve samples. Retain original samples outside merged windows.
Tests must set non-default threshold, window length, and handle ratio values and prove each option changes only its intended behavior.
- [ ] **Step 4: Run Bézier and regression checks**
@@ -516,7 +640,7 @@ git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/LocalCubicBez
git commit -m "feat: add local cubic bezier smoother"
```
### Task 7: Piecewise quintic Hermite smoother
### Task 8: Piecewise quintic Hermite smoother
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/PiecewiseQuinticSmoother.cs`
@@ -524,6 +648,7 @@ git commit -m "feat: add local cubic bezier smoother"
**Interfaces:**
- Implements: `IPathSmoother.Method == SmoothingMethod.PiecewiseQuintic`.
- Consumes: immutable knot and minimum-knot spacing from `SmoothingAlgorithmInput.Options`.
- Produces: C2-connected local polynomial segments without crossing direction boundaries.
- [ ] **Step 1: Write failing continuity and degeneracy tests**
@@ -550,6 +675,8 @@ p''(0)=a0, p''(1)=a1
Derive endpoint velocities from travel tangents times interval length. Blend shared accelerations once per knot and reuse the same value on both adjacent intervals. Reject singular or non-finite coefficients before sampling.
Map every interval sample to `s_ref = s_knot0 + t * (s_knot1 - s_knot0)` through `PathReferenceInterpolator`. Movement-bound excess returns `RetryableInfeasible`; singular coefficients, non-finite derivatives, and invalid spacing return `Failed`. Never clamp evaluated polynomial samples.
- [ ] **Step 4: Run quintic, geometry, and validator checks**
```powershell
@@ -568,7 +695,7 @@ git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/PiecewiseQuin
git commit -m "feat: add piecewise quintic path smoother"
```
### Task 8: Formal smoothing facade and explicit coarse-path fallback
### Task 9: Formal smoothing facade and explicit coarse-path fallback
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs`
@@ -580,7 +707,7 @@ git commit -m "feat: add piecewise quintic path smoother"
- [ ] **Step 1: Write failing facade tests**
Cover valid straight success, invalid input, cancellation, infeasible-without-fallback, and infeasible-with-verified-fallback. Assert fallback points use `CoarsePathFallback` and status never equals `Success`.
Cover valid straight success, invalid input, cancellation, infeasible-without-fallback, and infeasible-with-verified-fallback. Invalid input must include NaN, non-positive scales/windows/spacing, Bézier threshold outside `(0, π]`, and `KnotSpacingMeters < MinimumKnotSpacingMeters`. Assert fallback points use `CoarsePathFallback` and status never equals `Success`.
- [ ] **Step 2: Run and confirm RED**
@@ -605,7 +732,7 @@ private IPathSmoother Resolve(SmoothingMethod method) =>
};
```
Validate the coarse path before smoothing. On allowed fallback, convert the revalidated coarse points to `SmoothedPathPoint` with `CoarsePathFallback`, re-run shared geometry analysis, and return `Fallback`, preserving the failed method diagnostics.
Validate the full configuration and coarse path before constructing `SmoothingOptionsSnapshot` or starting finite retries; map every configuration-contract violation to `InvalidInput`. On allowed fallback, convert the revalidated coarse points to `SmoothedPathPoint` with `CoarsePathFallback`, re-run shared geometry analysis, and return `Fallback`, preserving the failed method diagnostics.
- [ ] **Step 4: Run all core smoothing verifications**
@@ -626,12 +753,14 @@ git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade ClumsyPilot/tests
git commit -m "feat: expose validated path smoothing service"
```
### Task 9: Comparison metrics, isolation, and deterministic ranking
### Task 10: Comparison metrics, isolation, and deterministic ranking
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonRequest.cs`
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonEntry.cs`
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonResult.cs`
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/SmoothingTimingSummary.cs`
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/StableGeometryDigest.cs`
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/SmoothingMethodRanker.cs`
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingComparisonService.cs`
- Test: `ClumsyPilot/tests/verify_path_smoothing_comparison.ps1`
@@ -639,6 +768,8 @@ git commit -m "feat: expose validated path smoothing service"
**Interfaces:**
- Produces: `Compare(PathSmoothingComparisonRequest, CancellationToken)`.
- Ranking order: feasible count; median variation energy; worst peak utilization; worst clearance loss; median length increase; median elapsed.
- Timing protocol: one unmeasured warm-up plus five measured deterministic executions per scenario and method; rank by measured median.
- `SmoothingTimingSummary` exposes a read-only five-value `MeasuredElapsedMilliseconds`, `MedianElapsedMilliseconds`, and `IsDeterministic`.
- [ ] **Step 1: Write failing comparison and ranking tests**
@@ -646,6 +777,8 @@ Create synthetic entries whose order changes at each tie-break level. Verify one
Also assert the result always contains one separately analyzed raw-path baseline plus exactly one entry for each requested method; the raw baseline is never treated as a candidate method in ranking.
For timing, assert the warm-up is excluded, exactly five samples remain, and a mismatch in status, point count, segment count, or stable geometry digest produces a non-deterministic diagnostic that excludes the method from recommendation.
- [ ] **Step 2: Run and confirm RED**
```powershell
@@ -671,6 +804,8 @@ double Median(IReadOnlyList<double> values)
Use absolute deltas when a raw denominator has magnitude below `1e-12`. The comparison service must force `AllowFallbackToCoarsePath = false` so fallback cannot masquerade as method success.
Run each method once for warm-up and five times for measurement against the same immutable prepared input. Use the first measured result as the canonical comparison geometry only after all five measured outputs match its stable status and geometry digest. `StableGeometryDigest` writes status, segment metadata, enum values, and every double through `BitConverter.DoubleToInt64Bits` in fixed little-endian order, then computes SHA-256; do not use `GetHashCode()`. Store all five elapsed values plus their median; only the median participates in the final lexicographic timing tie-break.
- [ ] **Step 4: Run comparison and service tests**
```powershell
@@ -688,7 +823,7 @@ git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison ClumsyPilot/P
git commit -m "feat: compare and rank smoothing methods"
```
### Task 10: Versioned fast fixtures and existing end-to-end scenarios
### Task 11: Versioned fast fixtures and existing end-to-end scenarios
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/SmoothingScenarioFixture.cs`
@@ -783,7 +918,7 @@ git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test ClumsyPilot/tests/g
git commit -m "test: add path smoothing scenarios and fixtures"
```
### Task 11: Shared figure model, IEEE SVG, and UTF-8 CSV
### Task 12: Shared figure model, IEEE SVG, and UTF-8 CSV
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/IeeeFigureStyle.cs`
@@ -796,6 +931,7 @@ git commit -m "test: add path smoothing scenarios and fixtures"
**Interfaces:**
- Consumes: immutable `PathSmoothingComparisonResult`, map, start/goal, scenario label.
- Produces: one immutable figure model, UTF-8 SVG, and UTF-8-BOM CSV.
- Portability boundary: text SVG is the editable master and requires exact fonts on the viewing machine; it is not claimed as a directly submittable IEEE vector file.
- [ ] **Step 1: Write failing style and serialization tests**
@@ -827,6 +963,8 @@ public const string LimitColor = "#CC79A7";
Allocate 60% width to panel `(a)`, split the right side between `(b)` curvature and `(c)` metrics, and compute one world-to-panel transform with equal X/Y scale.
Use `9 pt` for coordinate ticks, axis labels, legend, and table body; use `10 pt` for panel labels. Do not create any text smaller than `9 pt` at final physical size.
- [ ] **Step 4: Implement SVG and CSV**
SVG text must use explicit runs:
@@ -838,7 +976,7 @@ SVG text must use explicit runs:
CSV header order is fixed:
```text
ScenarioId,Method,Status,PathLengthMeters,MaximumAbsoluteVehicleCurvaturePerMeter,RootMeanSquareVehicleCurvaturePerMeter,TotalAbsoluteCurvatureVariationPerMeter,CurvatureVariationEnergy,MinimumBodyClearanceMeters,ElapsedMilliseconds,RetryCount,AcceptedStrength
ScenarioId,Method,Status,PathLengthMeters,MaximumAbsoluteVehicleCurvaturePerMeter,RootMeanSquareVehicleCurvaturePerMeter,TotalAbsoluteCurvatureVariationPerMeter,CurvatureVariationEnergy,MinimumBodyClearanceMeters,MedianElapsedMilliseconds,TimingSampleCount,IsDeterministic,RetryCount,AcceptedStrength
```
- [ ] **Step 5: Run SVG/CSV and comparison checks**
@@ -858,7 +996,7 @@ git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization ClumsyPilo
git commit -m "feat: render ieee smoothing svg and metrics"
```
### Task 12: Windows font validation and 300 dpi PNG export
### Task 13: Windows font validation and 600 dpi PNG export
**Files:**
- Modify: `ClumsyPilot/ClumsyPilot.csproj`
@@ -873,6 +1011,7 @@ git commit -m "feat: render ieee smoothing svg and metrics"
**Interfaces:**
- Produces: atomic `.svg`, `.png`, `.csv` export; report failures do not mutate comparison results.
- Runtime boundary: `SmoothingPngRenderer` is Windows-only; SVG/CSV remain usable without GDI+.
- Submission boundary: conversion of the verified SVG master to font-embedded or outlined PDF/EPS is explicit external publishing work, not a hidden exporter side effect.
- [ ] **Step 1: Add the failing PNG/font verification**
@@ -880,8 +1019,8 @@ The script must assert:
```text
PNG signature and CRC-valid chunks
IHDR width=2148 and height=1560
pHYs X=11811 and Y=11811 pixels/meter
IHDR width=4296 and height=3120
pHYs X=23622 and Y=23622 pixels/meter
SimSun and Times New Roman were resolved by exact family name
mixed sample "粗路径 κ(s) X (m) −π" produced non-empty glyph bounds
missing-font test returns FontUnavailable
@@ -926,7 +1065,7 @@ Return `FontUnavailable` before creating output files if either exact family is
- [ ] **Step 5: Render the shared model and write validated PNG**
Render at `2148 × 1560`, opaque white background, anti-aliased geometry, and no gradients/shadows. Convert bitmap pixels to RGBA and use the existing validated PNG path, extending it to insert a `pHYs` chunk with `11811` pixels/meter and correct CRC before `IDAT`.
Render at `4296 × 3120`, opaque white background, anti-aliased geometry, and no gradients/shadows. Convert bitmap pixels to RGBA and use the existing validated PNG path, extending it to insert a `pHYs` chunk with `23622` pixels/meter and correct CRC before `IDAT`.
Export all three files to temporary siblings, validate each, then rename into place. On failure, remove only those exact temporary siblings.
@@ -948,7 +1087,7 @@ git add -- ClumsyPilot/ClumsyPilot.csproj ClumsyPilot/ParkrobTrajplanner/PathSmo
git commit -m "feat: export ieee smoothing png reports"
```
### Task 13: Module documentation, report batch entry, and full verification
### Task 14: Module documentation, report batch entry, and full verification
**Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/README.md`
@@ -1028,7 +1167,7 @@ Expected: build exits 0 and every script prints its passed message with no termi
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\run_path_smoothing_comparison.ps1 -FixtureOnly
```
Inspect `straight`, `rectangle-detour`, and `forward-reverse-switch` PNG/SVG files. Confirm four-method legend order, equal path axes, readable 810 pt text, no Chinese mojibake, no clipping/overlap, curvature limits, and infeasible markers where applicable.
Inspect `straight`, `rectangle-detour`, and `forward-reverse-switch` PNG/SVG files. Confirm four-method legend order, equal path axes, readable 910 pt text, no Chinese mojibake, no clipping/overlap, curvature limits, and infeasible markers where applicable. Confirm the text SVG portability limitation and external PDF/EPS publishing step are documented.
- [ ] **Step 6: Commit**