# 粗路径平滑算法对比实验台设计

## 1. 背景

`ParkrobTrajplanner/CoarsePath` 已经通过 `CoarsePathPlanningService` 输出经过连续车体碰撞、曲率、终点和方向分段复核的 Hybrid A* 粗路径。成功的 `PlanningResult` 包含：

- 稠密的 `CoarsePathPoint`；
- 覆盖整条路径的前进/倒车 `PathSegment`；
- 累计弧长、车辆曲率、保守车体净空和换向标记；
- 地图、搜索和最终复核诊断。

粗路径由离散恒曲率运动原语拼接而成。它解决绕障拓扑、行驶方向和换向结构，但相邻原语之间的曲率可能跳变，不适合作为后续 SQP 的最终空间参考线。

本设计在 `CoarsePath` 之后增加独立的 `PathSmoothing` 模块。第一阶段先建设离线算法对比实验台，使用相同粗路径独立比较三次 B 样条、局部三次 Bézier 和分段五次多项式；这里的“独立”不表示并发执行。实验台统一执行几何分析、安全复核、指标排序和 IEEE 风格绘图。算法成熟后，正式规划流程只调用选定的默认平滑方法。

## 2. 目标

本阶段必须完成：

1. 定义稳定的粗路径平滑输入、输出、状态、配置和诊断契约。
2. 保持粗路径已经确定的绕障侧、前进/倒车顺序、换向次数和目标进入方向。
3. 使用三种互相独立的平滑方法处理同一条粗路径。
4. 用统一的几何分析器计算原粗路径和三种平滑路径的弧长、航向与曲率。
5. 对所有平滑候选重新执行完整车体碰撞、地图边界、最大车辆曲率、端点和方向分段复核。
6. 支持现有粗路径端到端案例和不重复运行 Hybrid A* 的快速粗路径夹具。
7. 为每个场景输出 IEEE 风格的四路径对比图、四曲率对比图和指标表。
8. 输出可供后续 SQP 使用的不可变平滑空间路径，不包含速度和时间参数。

## 3. 非目标

本阶段不包含：

- SQP、QP、Frenet 横向优化或速度规划；
- 时间戳、速度、加速度、角速度或 jerk；
- 改变 Hybrid A* 选定的绕障拓扑；
- 新增、删除或移动换向点；
- 现场实时重规划；
- 四舵轮角度、轮速和底盘跟踪控制；
- 把不可行平滑候选作为可执行路径发布；
- 用单一五次多项式覆盖任意长度的完整路径。

## 4. 总体架构

新增与 `CoarsePath` 平级的目录：

```text
ClumsyPilot/ParkrobTrajplanner/
├── CoarsePath/
└── PathSmoothing/
    ├── Contracts/
    ├── Processing/
    ├── Algorithms/
    ├── Validation/
    ├── Comparison/
    ├── Visualization/
    ├── Facade/
    └── Test/
```

各目录职责如下：

| 目录 | 职责 |
| --- | --- |
| `Contracts` | 平滑请求、结果、点、方向段、配置、状态、指标和诊断 |
| `Processing` | 输入校验、按方向拆段、等弧长重采样、关键点保护和统一几何分析 |
| `Algorithms` | 三次 B 样条、局部三次 Bézier、分段五次多项式及内部算法契约 |
| `Validation` | 完整车体碰撞、地图边界、曲率、端点、换向和输出不变量复核 |
| `Comparison` | 同输入多算法运行、结果隔离、指标汇总、排序和推荐 |
| `Visualization` | 图形场景模型、IEEE 样式、SVG/PNG/CSV 导出和字体检查 |
| `Facade` | 正式单算法平滑入口和离线多算法比较入口 |
| `Test` | 固定夹具、端到端案例和报告生成入口 |

`PathSmoothing` 只消费 `CoarsePath` 和 `Map` 的公开不可变数据，不读取传感器、定位、UI 或底盘。它不得修改已有 `PlanningResult`。

## 5. 数据流

```text
PlanningResult.Path + PlanningResult.Segments
PlanningGridMap + VehicleParameters + PathSmoothingConfiguration
                            │
                            ▼
                 输入契约和原粗路径安全复核
                            │
                            ▼
                 按 Forward/Reverse 独立拆段
                            │
                            ▼
              等弧长重采样、固定端点和换向点
                            │
                ┌───────────┼───────────┐
                ▼           ▼           ▼
          三次 B 样条   局部 Bézier   分段五次多项式
                │           │           │
                └───────────┼───────────┘
                            ▼
                 统一航向、弧长和曲率分析
                            │
                            ▼
          车体碰撞、边界、曲率、端点和分段复核
                            │
                            ▼
              指标汇总、方法排序和 IEEE 风格报告
```

三种方法不能串联。每种方法从同一份预处理输入开始，失败状态和重试次数分别记录。

## 6. 公共接口

### 6.1 正式单算法入口

```csharp
public sealed class PathSmoothingService
{
    public PathSmoothingResult Smooth(
        PathSmoothingRequest request,
        CancellationToken cancellationToken = default);
}
```

`PathSmoothingRequest` 包含：

```text
CoarsePath       IReadOnlyList<CoarsePathPoint>
Segments         IReadOnlyList<PathSegment>
Map              PlanningGridMap
Vehicle          VehicleParameters
Configuration    PathSmoothingConfiguration
```

调用方可以从成功的 `PlanningResult.Path` 和 `PlanningResult.Segments` 直接构造请求。请求仍需携带规划时使用的地图和车辆参数，因为平滑路径必须独立进行安全复核。

### 6.2 离线比较入口

```csharp
public sealed class PathSmoothingComparisonService
{
    public PathSmoothingComparisonResult Compare(
        PathSmoothingComparisonRequest request,
        CancellationToken cancellationToken = default);
}
```

比较请求复用同一份粗路径、地图、车辆和公共配置，并明确列出需要运行的方法。第一版默认方法集合固定为：

```text
CubicBSpline
LocalCubicBezier
PiecewiseQuintic
```

比较服务不绘图。报告导出器只消费不可变比较结果，因此绘图失败不会改变算法状态或平滑路径。

## 7. 核心契约

### 7.1 平滑方法

```text
SmoothingMethod:
    CubicBSpline
    LocalCubicBezier
    PiecewiseQuintic
```

### 7.2 平滑状态

```text
PathSmoothingStatus:
    Success
    FallbackToCoarsePath
    InvalidInput
    Infeasible
    Failed
    Cancelled
```

- `Success`：平滑路径通过所有复核。
- `FallbackToCoarsePath`：所有允许的平滑尝试失败，且原粗路径重新通过安全复核后，按显式配置返回原路径。
- `InvalidInput`：输入路径、方向段、地图、车辆或配置不符合契约。
- `Infeasible`：算法产生了有限数值候选，但候选未通过安全或运动学复核。
- `Failed`：算法发生数值退化或无法产生完整候选。
- `Cancelled`：调用被取消，不能发布部分路径。

除 `Success` 和 `FallbackToCoarsePath` 外，正式 `PathSmoothingResult.Path` 和 `Segments` 必须为空。

### 7.3 平滑路径点

`SmoothedPathPoint` 是不可变值，包含：

```text
X、Y                    世界坐标，m
Heading                 归一化车头航向，rad
UnwrappedHeading        连续展开车头航向，rad
ArcLength               从完整路径起点累计的非负弧长，m
Direction               Forward 或 Reverse
GeometricCurvature      按行进弧长计算的有符号几何曲率，1/m
VehicleCurvature        车辆模型使用的有符号曲率，1/m
BodyClearance           扩大车体的保守净空下界，m
IsGearSwitchPoint       是否为新方向段开始的换向点
Source                  Anchor、Interpolated、GearSwitch 或 CoarsePathFallback
```

回退原粗路径时 `Source` 使用单独的 `CoarsePathFallback`，避免把未经平滑的普通粗路径点错误标记为算法插值点。

设 `directionSign` 在前进时为 `+1`，倒车时为 `-1`。每个方向段内部采用：

```text
GeometricCurvature = d(Heading) / ds
VehicleCurvature   = directionSign × GeometricCurvature
```

曲率不得跨合法换向重复点求导。

### 7.4 平滑方向段

平滑后点数与粗路径不同，不能直接复用原 `PathSegment` 索引。`SmoothedPathSegment` 包含：

```text
SegmentIndex
Direction
StartIndex
EndIndex
StartsAtGearSwitch
EndsAtGearSwitch
```

其索引必须连续、无重叠并完整覆盖平滑路径。

### 7.5 平滑配置

公共配置至少包含：

```text
Method
OutputSpacingMeters                 默认 0.05
MaximumCollisionCheckStepMeters    默认 0.025
MinimumClearanceReserveMeters      默认 0.02
AllowFallbackToCoarsePath          默认 true
SmoothingStrength                  默认 1.00
RetryStrengthScales
```

`RetryStrengthScales` 是有限、严格递减的只读集合。第一版建议为：

```text
1.00, 0.75, 0.50, 0.25
```

每种算法拥有独立的强类型子配置，不能用无语义的字符串字典传参。第一版固定默认值如下：

```text
CubicBSpline.EndpointTangentScale                       1/3
LocalCubicBezier.CornerHeadingThresholdRadians         π/18
LocalCubicBezier.MaximumWindowLengthMeters             0.60
LocalCubicBezier.HandleLengthRatio                     1/3
PiecewiseQuintic.KnotSpacingMeters                     0.50
PiecewiseQuintic.MinimumKnotSpacingMeters              0.10
```

公共请求在构造时复制这些配置；进入数值算法前再次转换成仅含只读标量的内部快照。算法不得硬编码或回读调用方可变配置。

所有配置数值必须有限；`OutputSpacingMeters`、`MaximumCollisionCheckStepMeters`、`SmoothingStrength`、控制比例及所有窗口/结点长度必须为正，`MinimumClearanceReserveMeters` 必须非负，转角阈值必须位于 `(0, π]`，且 `KnotSpacingMeters >= MinimumKnotSpacingMeters`。不满足时返回 `InvalidInput`，不得进入有限重试。

`MaximumCollisionCheckStepMeters` 默认值为当前粗路径默认安全精度 `0.025 m`。若上游粗规划采用了更小的检查步长，调用方必须传入相同或更小的值；平滑模块不能从未携带的粗规划配置中猜测该参数。

## 8. 公共预处理

### 8.1 输入校验

处理前必须验证：

- 粗路径和方向段均非空；
- 所有数值有限；
- 弧长非负且不递减；
- 唯一允许的相邻重复位姿是合法换向对；
- 方向段完整覆盖粗路径；
- 换向点的位置、航向和弧长保持成对一致；
- 地图可用于规划；
- 车辆尺寸、安全余量和最大曲率有效；
- 原粗路径在当前地图和车辆参数下仍然安全。

原粗路径复核失败时不得执行平滑，也不得回退。

### 8.2 方向拆分

每个 `PathSegment` 独立处理。方向段首尾点全部是强制锚点。换向两侧的重复位姿分别属于相邻方向段，两个点都必须保留。

### 8.3 重采样

所有方向段先转换为统一的等弧长采样。默认间距为 `0.05 m`，最后一个不足整步的区间保留精确段尾。

重采样不能跨换向点，不能改变段首、段尾的位置和车头航向。

### 8.4 安全移动范围

预处理器根据原路径的保守车体净空和 `MinimumClearanceReserveMeters` 计算每个样点允许的最大移动范围。该范围只用于减少平滑曲线切弯进入障碍物的概率，不构成安全证明。最终安全性只能由完整车体碰撞和扫掠复核确认。

算法生成候选时，每个求值输出点还必须相对同一原始弧长位置的插值参考点复核该范围：`max(0, reference.BodyClearance - MinimumClearanceReserveMeters)`。参数映射统一使用方向段局部原始弧长：完整方向段的归一化参数 `u∈[0,1]` 映射为 `s_ref=u×L_original`；局部窗口参数 `t∈[0,1]` 映射为 `s_ref=s_entry+t×(s_exit-s_entry)`；分段五次区间采用相同的结点弧长插值。禁止使用原始点索引比例代替弧长映射。

求值点超限属于可重试的几何不可行：该次尝试不得发布候选几何，但运行器必须继续下一个较低强度。禁止把单个求值点投影或硬裁剪回允许范围，因为这会在原折点附近破坏曲线切线连续性。该候选可行性门槛不替代最终的完整车体碰撞和扫掠复核。

## 9. 三种平滑方法

### 9.1 三次 B 样条

每个方向段使用夹持三次 B 样条近似：

- 固定段首和段尾位置；
- 使用行进方向切向约束段首和段尾；
- 不把换向两侧合并成一条曲线；
- 通过平滑强度控制控制点对原粗路径的贴合程度；
- 输出后统一按弧长重采样。

该方法是普通长路径段的主要候选，优点是整体连续、控制点数量适中；风险是绕障拐角处可能切弯。

### 9.2 局部三次 Bézier

先从统一重采样路径中识别超过配置航向变化阈值的局部转折区域，再使用三次 Bézier 替换该区域：

- 区域外路径保持不变；
- 每个局部连接固定入口和出口位置；
- 控制柄沿入口和出口切向；
- 相邻局部区域重叠时先合并，避免重复处理；
- 直线和微小转折不创建无意义 Bézier 段。

该方法计算最快且局部可控，但只能保证所设计连接阶次内的连续性，整体曲率质量需要通过统一指标判断。

### 9.3 分段五次多项式

五次方法采用分段二维 Hermite 形式，而不是用单条五次多项式拟合完整方向段：

- 每个子段约束首尾位置；
- 首尾一阶导数由行进切向确定；
- 首尾二阶导数由相邻曲率估计并进行连续化；
- 共享内部结点的一阶和二阶导数保持一致；
- 固定完整方向段的首尾位置、航向和曲率约束。

该方法追求二阶连续，适合终端连接和曲率连续性要求较高的参考线，但参数敏感且更容易在短段上数值退化。

## 10. 几何分析

原粗路径、三种候选和平滑回退路径全部通过同一个 `PathGeometryAnalyzer`。分析器逐方向段计算，禁止跨换向点差分。

分析器负责：

1. 按位置重新计算累计弧长；
2. 根据行进切向和方向恢复车头航向；
3. 连续展开航向；
4. 计算几何曲率和车辆曲率；
5. 计算曲率总变化和曲率变化能量；
6. 生成完整路径和方向段索引。

原 `CoarsePathPoint.VehicleCurvature` 仍保留为粗规划诊断，但算法比较不直接拿它与重新计算的平滑曲率混用。

## 11. 平滑安全复核

`SmoothedPathValidator` 必须独立复核：

- 所有点和派生数值有限；
- 起点、终点位置和车头航向与粗路径对应端点一致；
- 每个换向点的位置和车头航向不变；
- 前进/倒车段数量、顺序和边界拓扑不变；
- 累计弧长严格增加，合法换向重复点除外；
- `abs(VehicleCurvature)` 不超过车辆最大曲率；
- 每个点的扩大车体无碰撞且位于地图内；
- 相邻点之间的完整车体扫掠无碰撞；
- 发布的 `BodyClearance` 不高于复核得到的保守净空；
- 平滑方向段完整覆盖输出。

验证器复用现有车辆足迹和碰撞语义，不能引入另一套障碍膨胀规则。

## 12. 有限安全回退

每种方法先按 `SmoothingStrength` 运行。候选不通过复核或算法内的可动范围门槛时，依次使用 `RetryStrengthScales` 降低平滑强度。内部算法结果必须区分：

- `Success`：产生完整有限候选，进入统一分析和安全复核；
- `RetryableInfeasible`：可动范围、局部几何约束或安全复核不满足，记录原因后尝试下一强度；
- `Failed`：非法数值、奇异系数、输入契约错误或无法构造完整候选，立即停止该方法。

```text
默认强度
    ↓ 可重试不可行
0.75 倍
    ↓ 可重试不可行
0.50 倍
    ↓ 可重试不可行
0.25 倍
    ↓ 可重试不可行
该方法标记 Infeasible
```

重试次数、采用强度和每次失败原因必须进入诊断。达到有限次数后必须停止，不能无限调参。算法返回 `Failed` 时不得用降低强度掩盖数值或契约错误。

正式单算法服务只有在 `AllowFallbackToCoarsePath = true` 且原粗路径重新通过完整安全复核时，才能返回 `FallbackToCoarsePath`。比较实验中的方法失败不能被回退结果伪装成该方法成功。

## 13. 质量指标

每条原始或平滑路径都计算：

```text
Feasible
PathLengthMeters
MaximumAbsoluteVehicleCurvaturePerMeter
RootMeanSquareVehicleCurvaturePerMeter
TotalAbsoluteCurvatureVariationPerMeter
CurvatureVariationEnergy
MinimumBodyClearanceMeters
ComputationElapsed
RetryCount
AcceptedStrength
```

曲率变化指标逐方向段计算后累加，不跨换向点产生虚假的曲率跳变。

正式单算法结果中的 `ComputationElapsed` 记录一次规范执行的耗时，不参与跨方法推荐。离线比较的耗时排序使用独立基准流程：每个场景和方法先预热 `1` 次，再计时 `5` 次；五次输出必须具有相同状态、路径点数和稳定几何摘要，否则该方法标记为非确定性诊断失败。稳定几何摘要按状态、方向段元数据和所有路径点字段的 IEEE 754 位模式顺序生成 SHA-256，不使用进程相关的 `GetHashCode()`。排名使用五次计时的中位数，预热和计时运行均不得改变正式比较结果。

比较条目另外保存 `MeasuredElapsedMilliseconds[5]`、`MedianElapsedMilliseconds`、`TimingSampleCount=5` 和 `IsDeterministic`；CSV 和排序使用 `MedianElapsedMilliseconds`，不能把正式单次 `ComputationElapsed` 混作基准中位数。

每个平滑结果还报告相对于原粗路径的：

```text
LengthChangePercent
PeakCurvatureChangePercent
CurvatureVariationChangePercent
MinimumClearanceChangeMeters
```

任何分母接近零的相对指标必须使用明确定义的绝对值替代，不能产生 NaN 或无穷值。

## 14. 方法排序

方法选择采用可解释的字典序，不使用隐藏的加权总分。对全部必选场景按以下聚合值依次比较：

1. 安全可行场景数量更多；
2. 曲率变化能量的场景中位数更低；
3. 各场景最大曲率利用率中的最坏值更低；
4. 各场景最小净空损失中的最坏值更小；
5. 路径长度增量的场景中位数更小；
6. 计算耗时的场景中位数更短。

完全相同时使用稳定的方法枚举顺序打破平局，并在报告中显示所有原始指标。若没有方法通过必选场景，比较结果不推荐默认方法。

## 15. 场景体系

### 15.1 端到端场景

端到端组使用现有 `CoarsePathScenarioFactory` 的固定基准入口，不读取实时 AMR：

| 场景 | 用途 |
| --- | --- |
| `ExplicitEmpty` | 直线不应被无意义扭曲 |
| `RectangleDetour` | 验证绕障转折和平滑切弯 |
| `ManualAndTwoLeg` | 验证多来源障碍和复杂路径 |
| `ReverseGearSwitch` | 验证方向分段和换向点保护 |

`CacheHit` 不增加新的平滑几何，不进入算法比较。`NoFeasiblePath` 没有成功粗路径，只保留为上游粗路径测试。

### 15.2 快速粗路径夹具

快速夹具覆盖：

```text
straight
single-turn
s-bend
large-heading-change
rectangle-detour
multi-obstacle-detour
narrow-corridor
forward-reverse-switch
```

每个夹具保存：

- 稳定的英文场景 ID 和版本；
- 对应的固定地图重建信息；
- 车辆参数；
- 粗路径点和方向段；
- 生成该路径时的粗规划配置指纹；
- 期望的起终点、方向段数和换向点数。

快速实验重建地图但不调用 `HybridAStarPlanner`。夹具指纹与当前预期不一致时返回“夹具已过期”，不能静默继续，也不能在普通测试中自动覆盖夹具。

夹具更新必须通过显式开发入口完成，并在更新后运行端到端复核。

## 16. 比较报告

### 16.1 四条曲线

正常场景同时比较：

1. 原始粗路径；
2. 三次 B 样条；
3. 局部三次 Bézier；
4. 分段五次多项式。

路径图和曲率图使用相同的四方法顺序。某种方法产生完整但不可行的候选时，实验报告可以绘制其候选并在违规位置标记叉号，同时明确标注“不可行”。该候选只能存在于比较报告的只读调试数据中，不能进入正式 `PathSmoothingResult.Path`。

算法内可行性门槛或数值构造失败导致没有完整候选时，保留图例和指标行，显示“无有效曲线”，不得伪造曲线。

### 16.2 IEEE 风格版式

每个场景输出一张双栏宽度图：

```text
物理尺寸：7.16 × 5.2 in
PNG：4296 × 3120 px
分辨率：600 dpi
```

布局：

```text
┌─────────────────────┬───────────────┐
│ (a) 四条路径对比     │ (b) 四条曲率  │
│ 地图、障碍物、起终点 │ κ(s) 与限制线 │
│                     ├───────────────┤
│                     │ (c) 指标表     │
└─────────────────────┴───────────────┘
```

- 路径面板约占宽度的 60%；
- 路径面板强制 X/Y 等比例；
- 曲率横轴使用各路径自身的累计弧长 `s (m)`；
- 曲率面板显示车辆最大正、负曲率限制；
- 不放大标题，只使用紧凑的 `(a)`、`(b)`、`(c)`；
- 路径面板不使用背景网格；
- 曲率面板只保留零线、必要刻度和曲率限制线；
- 指标表小数点对齐，失败项显示状态而非虚构数值；
- 图例在所有场景中位置、顺序、颜色和线型一致。

IEEE 官方图形指南建议使用颜色和线型共同编码、保持字体和字号一致，并优先使用高分辨率或矢量图：

- https://conferences.ieeeauthorcenter.ieee.org/write-your-paper/improve-your-graphics/
- https://journals.ieeeauthorcenter.ieee.org/create-your-ieee-journal-article/create-graphics-for-your-article/resolution-and-size/

### 16.3 固定色卡

采用色盲友好的科学绘图色卡：

| 内容 | 颜色 | 线型 |
| --- | --- | --- |
| 原始粗路径 | `#4D4D4D` 深灰 | 虚线 |
| 三次 B 样条 | `#0072B2` 深蓝 | 实线 |
| 局部三次 Bézier | `#D55E00` 朱红 | 点划线 |
| 分段五次多项式 | `#009E73` 蓝绿色 | 点线 |
| 曲率上下限 | `#CC79A7` 紫红 | 细虚线 |
| 障碍物 | `#D9D9D9` 填充、`#666666` 描边 | 实线 |

起点使用带黑色边框的黄色圆形，终点使用带黑色边框的紫色菱形。图中不使用渐变、阴影或依赖透明度才能辨认的叠色。

### 16.4 字体、字号和防乱码

用户指定：

```text
中文：SimSun（宋体）
英文、数字、希腊字母和公式：Times New Roman
```

绘图实现必须：

1. 在导出前按准确字体族名检查两种字体；
2. 将混合文本按中文和非中文字符片段拆分；
3. 使用字体度量统一片段基线；
4. 使用同一排版模型生成 SVG 和 PNG；
5. 字体缺失时返回明确的 `FontUnavailable` 导出失败，不能静默替换；
6. SVG 使用 UTF-8 XML；
7. CSV 使用带 BOM 的 UTF-8；
8. PNG 写入并验证对应 600 dpi 的物理分辨率元数据；
9. 使用稳定英文文件名，中文只出现在图内和 CSV 内容中。

最终物理尺寸下建议：

```text
坐标刻度：9 pt
坐标标题、图例和表格正文：9 pt
分图编号：10 pt
```

英文 Times New Roman 属于 IEEE 推荐字体。由于宋体不是 IEEE 通用英文字体清单的一部分，它只用于满足本项目中文图注需求；英文、数字和数学字符仍使用 Times New Roman。

字体检查只能保证生成机器正确渲染。文本型 SVG 是可编辑母版，仅保证在安装了 `SimSun` 和 `Times New Roman` 的环境中保持原排版；600 dpi PNG 是无需字体依赖的便携预览。若用于 IEEE 正式投稿，必须在模块外将已验证的 SVG 转换为 IEEE 接受的 PDF/EPS，并嵌入字体或将文字转换为轮廓；本阶段不把 SVG 声明为可直接投稿格式。

### 16.5 输出格式

每个场景输出：

```text
<scenario-id>-comparison.svg
<scenario-id>-comparison.png
<scenario-id>-metrics.csv
```

SVG 是可编辑矢量母版，600 dpi PNG 用于无需字体依赖的直接查看和现有工作流，CSV 保存每种方法的完整指标。报告输出属于生成产物，不提交到源码目录。IEEE 投稿用 PDF/EPS 的字体嵌入或轮廓化转换属于显式发布步骤，不在本模块中静默完成。

绘图使用独立的不可变 `SmoothingFigureModel`。SVG 和 PNG 渲染器都消费该模型，以保证面板范围、曲线、颜色、字体和文本一致。

## 17. 错误隔离

- 单个算法的预期失败只影响对应比较项；
- 单个算法发生数值异常时，比较服务记录算法名、异常类型和安全诊断，然后继续其他算法；
- 取消优先于继续比较，取消后不再启动后续算法；
- 报告导出失败不改变平滑比较结果；
- 某个场景所有算法失败时仍可生成只包含原粗路径、失败状态和诊断的报告；
- 正式服务不捕获并隐藏调用方编程错误，但所有输入契约错误必须转换为稳定状态和诊断；
- 所有错误消息包含场景、方法和处理阶段，不依赖堆栈才能定位。

## 18. 测试设计

### 18.1 契约测试

- 空路径、空方向段、空地图、空车辆和空配置；
- NaN、无穷值、负弧长和非法曲率；
- 非法重复点和不完整方向段；
- 成功和失败结果的只读集合；
- 失败状态不得携带可执行路径；
- 取消不得发布部分路径。

### 18.2 几何分析测试

- 直线的曲率为零；
- 已知半径圆弧的几何曲率和车辆曲率符合方向语义；
- 前进和倒车使用正确的曲率符号；
- 航向跨越 `-π/π` 时连续展开；
- 不跨换向重复点计算差分；
- 等弧长重采样保留精确段尾。

### 18.3 算法性质测试

三种方法分别验证：

- 直线路径不产生无意义横向偏移；
- 方向段首尾位置和航向保持；
- 换向点的位置、航向和顺序保持；
- 输出点数和间距符合配置；
- 重试强度严格递减且次数有限；
- 可动范围超限进入下一强度，数值失败立即停止；
- 非均匀原始采样仍按方向段局部弧长映射参考点；
- 修改请求构造后的外部配置不影响内部算法快照；
- 短段和退化控制点产生明确失败而非 NaN。

### 18.4 安全复核测试

- 平滑切入障碍物时拒绝；
- 相邻点之间扫掠碰撞时拒绝；
- 车体越过地图边界时拒绝；
- 最大车辆曲率超限时拒绝；
- 净空被高估时拒绝；
- 方向段或换向结构改变时拒绝；
- 原粗路径复核失败时禁止回退。

### 18.5 比较和夹具测试

- 三种方法接收同一份预处理输入；
- 单个方法失败不影响其他方法；
- 排序规则按固定字典序执行；
- 每个方法预热一次、计时五次并使用中位数，非确定性输出不得参与推荐；
- 没有可行方法时不推荐默认方法；
- 快速夹具路径不调用 Hybrid A*；
- 配置指纹变化会使夹具明确过期；
- 四个现有端到端场景保持预期成功和方向结构。

### 18.6 绘图测试

- 固定色值、线型、图例顺序和面板编号；
- 路径图 X/Y 比例相等；
- 曲率限制线数值正确；
- `SimSun` 与 `Times New Roman` 检测；
- `粗路径 κ(s) X (m) −π` 等代表性混合文本完整渲染；
- SVG 为有效 UTF-8，包含预期字体和所有四种方法；
- PNG 具有 `4296 × 3120` 像素、正确 PNG 结构、CRC 和 600 dpi 元数据；
- CSV 具有 UTF-8 BOM、稳定列顺序和不依赖区域设置的小数格式；
- 不使用依赖平台抗锯齿细节的脆弱逐像素金图测试；
- 至少对直线、绕障和换向三张代表图执行人工视觉检查，确认无乱码、遮挡、裁切和间距失衡。

## 19. 实施边界

第一轮实施只建立本设计中的接口、三种算法、统一验证、快速夹具和报告。正式 `MovementTest` 集成是后续独立工作，在默认方法通过离线比较并得到明确推荐后进行。

实验台不得让 `CoarsePath` 依赖 `PathSmoothing`。正式串联由更上层门面完成：

```text
CoarsePathPlanningService
        ↓ 成功 PlanningResult
PathSmoothingService
        ↓ 成功或明确回退
后续 SQP
```

## 20. 验收标准

设计实施完成需满足：

1. 成功粗路径能通过单一公共接口得到不可变平滑路径结果。
2. 三种方法对同一粗路径独立运行，不串联、不修改输入。
3. 起终点、方向段和换向结构在所有成功结果中保持。
4. 所有成功结果通过完整车体碰撞、边界、曲率和输出契约复核。
5. 不可行候选不会进入正式可执行路径。
6. 八个快速夹具无需运行 Hybrid A* 即可完成三算法比较。
7. 四个现有端到端场景能够从建图和 Hybrid A* 连接到平滑比较。
8. 每个场景生成包含四路径、四曲率和指标表的 SVG、600 dpi PNG 与 UTF-8 CSV，并明确 SVG/PDF 的投稿边界。
9. 图内中文使用宋体，英文、数字和数学字符使用 Times New Roman，代表性图无乱码、遮挡或裁切。
10. 比较结果使用公开的字典序规则推荐方法；没有合格方法时明确不推荐。
11. 输出可以作为后续 SQP 的空间参考路径输入，但不提前引入时间、速度或控制字段。
