docs: gate Local G2 pipeline on stable region order

This commit is contained in:
梁薄云
2026-07-31 16:05:21 +08:00
parent e8c4704d87
commit 5b072c0e35
@@ -1189,6 +1189,7 @@ git commit -m "feat: validate local G2 candidate quality"
**Files:** **Files:**
- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2PreSmoothingPipeline.cs` - Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2PreSmoothingPipeline.cs`
- Consume unchanged: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/LocalG2/LocalG2RegionWorkOrder.cs`
- Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs` - Modify: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs`
- Create: `ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1` - Create: `ClumsyPilot/tests/verify_path_smoothing_local_g2_integration.ps1`
- Modify: `ClumsyPilot/tests/verify_path_smoothing_service.ps1` - Modify: `ClumsyPilot/tests/verify_path_smoothing_service.ps1`
@@ -1196,6 +1197,8 @@ git commit -m "feat: validate local G2 candidate quality"
**Interfaces:** **Interfaces:**
- Consumes: validated request, prepared path and fair raw baseline. - Consumes: validated request, prepared path and fair raw baseline.
- Consumes `LocalG2RegionWorkOrder.TryCreate(...)`; pipeline iteration must not use report-order regions directly.
- Publishes region reports in the detector's original ascending order, independently of processing order.
- Produces: - Produces:
```csharp ```csharp
@@ -1242,6 +1245,21 @@ Assert-True ($result.Diagnostics.Metrics.MinimumBodyClearanceMeters -ge 0.02) `
For `Complete` and `PartialImprovement`, require at least one region report with `Improved`. For `PartialImprovement`, require at least one `RetainedOriginal`. Run every fixture twice and compare status, point count, coordinates and region reports. For `Complete` and `PartialImprovement`, require at least one region report with `Improved`. For `PartialImprovement`, require at least one `RetainedOriginal`. Run every fixture twice and compare status, point count, coordinates and region reports.
Create one deterministic same-direction fixture with two disjoint detected
regions. Both accepted candidates must change their local replacement length.
Assert:
1. the larger-original-arc region is evaluated first;
2. the second processed region still matches the intended original front
window endpoints;
3. both reports are `Improved`;
4. reports are published in ascending original arc order;
5. both Local G2 replacements are present in the final full path;
6. two identical requests produce equal status, candidate indices, report
order, point count and point coordinates.
The test must fail if the pipeline replaces `workRegions` with `regions`.
Also create two explicit service cases: Also create two explicit service cases:
```powershell ```powershell
@@ -1273,31 +1291,68 @@ Expected: fail because the service cannot resolve `LocalG2Quintic`.
Pipeline pseudocode must be implemented directly: Pipeline pseudocode must be implemented directly:
```csharp ```csharp
if (!_workOrder.TryCreate(
regions,
out IReadOnlyList<LocalG2SmoothingRegion> workRegions,
out string orderReason))
{
return PathSmoothingResult.Failure(
PathSmoothingStatus.Failed,
new PathSmoothingDiagnostics(
new PathQualityMetrics(),
TimeSpan.Zero,
0,
0d,
orderReason));
}
PreparedPath current = preparedPath; PreparedPath current = preparedPath;
var reports = new List<PathSmoothingRegionReport>(); var reportsByRegion = new Dictionary<LocalG2SmoothingRegion, PathSmoothingRegionReport>();
int improvedCount = 0; int improvedCount = 0;
foreach (LocalG2SmoothingRegion region in regions) foreach (LocalG2SmoothingRegion region in workRegions)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
IReadOnlyList<LocalG2CandidateGeometry> candidates = IReadOnlyList<LocalG2CandidateGeometry> candidates =
_builder.Build(preparedPath.Segments[region.SegmentIndex], region, outputSpacing, options, cancellationToken); _builder.Build(
preparedPath.Segments[region.SegmentIndex],
region,
outputSpacing,
options,
cancellationToken);
var evaluations = new List<LocalG2CandidateEvaluation>(); var evaluations = new List<LocalG2CandidateEvaluation>();
foreach (LocalG2CandidateGeometry candidate in candidates) foreach (LocalG2CandidateGeometry candidate in candidates)
evaluations.Add(_evaluator.Evaluate(preparedPath, current, region, candidate, request, options, cancellationToken)); {
evaluations.Add(
_evaluator.Evaluate(
preparedPath,
current,
region,
candidate,
request,
options,
cancellationToken));
}
LocalG2CandidateEvaluation best = LocalG2CandidateEvaluator.SelectBest(evaluations); LocalG2CandidateEvaluation best =
LocalG2CandidateEvaluator.SelectBest(evaluations);
if (best != null && best.Accepted) if (best != null && best.Accepted)
{ {
current = best.SplicedPreparedPath; current = best.SplicedPreparedPath;
improvedCount++; improvedCount++;
reports.Add(CreateImprovedReport(region, best)); reportsByRegion.Add(region, CreateImprovedReport(region, best));
} }
else else
{ {
reports.Add(CreateRetainedReport(region, evaluations)); reportsByRegion.Add(
region,
CreateRetainedReport(region, evaluations));
} }
} }
var reports = new List<PathSmoothingRegionReport>(regions.Count);
for (int reportIndex = 0; reportIndex < regions.Count; reportIndex++)
reports.Add(reportsByRegion[regions[reportIndex]]);
``` ```
Regions are disjoint after merging, so successful earlier replacements remain when a later region fails. Regions are disjoint after merging, so successful earlier replacements remain when a later region fails.