8.5 KiB
粗路径手动测试诊断与耗时设计
目标
完善 [MovementTest(name = "粗路径规划")] 的手动测试体验,使调用者能够为每次测试指定正数秒总预算,并在失败时看到真实、可区分的终止原因、搜索统计、总耗时和路径搜索耗时。
本改动不放宽碰撞安全、终点判定或成功路径发布条件,也不把普通规划失败改成异常。普通失败继续通过 PlanningResult 返回;输入读取失败和未预期程序错误才按异常信息展示。
已确认的现状
手动测试使用正式的 CoarsePathPlanningService、HybridAStarPlanner 和 HybridAStarSearch,没有测试专用搜索器,也没有关闭倒车;HybridAStarConfiguration.AllowReverse 默认仍为 true。
影响实际成功率和问题定位的限制如下:
- 手动入口未覆盖
SearchTimeout,因此沿用 5 秒总预算。 - 已复现一个空图可行案例:起点
(1000 mm, 2000 mm, 0 deg)、终点(1500 mm, 2500 mm, 90 deg)在 5 秒返回SearchTimeout,将预算改为 30 秒后约 10.2 秒成功。 - 手动测试固定使用长
0.80 m、宽0.60 m、安全余量0.05 m、最小转弯半径1.20 m的演示车辆参数,不读取现场 AMR 几何参数。 HybridAStarPlanner将所有搜索阶段失败改写成同一句“Hybrid A* 搜索未找到可发布路径”,丢失了超时、节点上限、无解和内部错误的具体区别。- 状态图层只在
TerminationReason非空时绘制原因,而 Toast 不包含原因。 - 第一版算法只支持汽车式恒曲率前进、倒车和原语边界换向;明确不支持 Reeds-Shepp 精确连接、横移、蟹行和原地旋转。这是整个粗规划核心的版本边界,不是手动测试单独阉割。
范围
包含
- 手动测试增加单次总超时输入。
- 搜索结果保留原始终止原因。
- 面向调用者组合状态、原因、资源上限和节点统计。
- 状态图层与 Toast 展示原因、总耗时和路径搜索耗时。
- README 说明测试车辆参数和第一版运动能力限制。
- 自动化覆盖输入校验、失败分类、耗时边界和 UI 文本。
不包含
- Reeds-Shepp 或 Dubins 解析终点连接。
- 横移、蟹行或原地旋转原语。
- 路径平滑、速度规划或控制。
- 将固定演示车辆参数替换为尚未定义来源的现场 AMR 参数。
- 改变地图边界策略、碰撞规则、终点容差或 Open List 排序。
设计
手动超时输入
CoarsePathPlanningTest.Test() 在读取终点航向后读取“粗路径规划总超时(秒,必须大于 0)”。输入按当前文化和不变文化解析为有限 double,必须严格大于零,并且转换为 TimeSpan 后不溢出。
场景工厂仍负责创建完整业务请求;测试入口在取得 CoarsePathPlanningJob 后只覆盖本次请求的:
job.Configuration.SearchTimeout = TimeSpan.FromSeconds(timeoutSeconds);
固定回归场景继续使用各自既有预算。手动输入不修改全局默认配置,也不影响下次测试。
输入 0、负数、NaN、Infinity、非数字或超出 TimeSpan 可表示范围时,本次规划不启动,并通过既有输入失败提示显示具体字段。
搜索原始终止原因
HybridAStarSearchResult 增加只读 TerminationReason。搜索的每个失败出口填写与发生位置一致的原因:
SearchTimeout:共享总预算已经耗尽。SearchNodeLimitExceeded:扩展节点数达到配置上限。NoFeasiblePath:二维启发式不可达、起始状态无法进入 Open List,或 Open List 耗尽。Cancelled:收到取消请求。- 输入、地图、车辆或配置状态:指出对应校验阶段。
InternalError:保留异常类型和消息,不在 UI 展示完整堆栈。
HybridAStarPlanner 不再用同一句泛化说明覆盖搜索原因。它将原始原因与可操作统计组合为最终 PlanningDiagnostics.TerminationReason。例如:
总预算 8.000 秒已耗尽;扩展 5,366,生成 21,014,Open List 峰值 15,102。
节点上限原因包含配置的 MaximumExpandedNodes;无解原因包含扩展、生成和 Open List 峰值。起点、终点、碰撞和最终复核的现有明确原因保持不变。
异常语义
普通规划失败不抛异常,因为超时、无解、碰撞和资源上限是可预期业务结果。调用者继续通过 PlanningStatus 做稳定分支,并读取 TerminationReason。
搜索或规划器捕获未预期异常时返回 InternalError,原因包含异常类型和非空消息。完整堆栈不进入 Toast,避免界面噪声;自动化测试验证异常不会再次被完全静默吞掉。
耗时
保留既有 PlanningDiagnostics.Elapsed,定义为从 CoarsePathPlanningService.Plan 入口开始的总耗时,包括建图、缓存查询、栅格化、距离场和路径规划。
新增只读 PlanningDiagnostics.PathSearchElapsed,边界为:
- 开始:规划请求、地图、车辆、配置、起点、终点和初始碰撞预检均已通过,即将进入
HybridAStarSearch.Search。 - 包含:二维 Dijkstra 启发式、Hybrid A* 扩展、回溯、路径装配、方向分段和最终复核。
- 结束:
HybridAStarPlanner准备构造最终PlanningResult。 - 不包含:地图来源读取、地图缓存、栅格化、距离场构建和搜索前预检。
搜索开始前失败时为 TimeSpan.Zero;搜索开始后的成功、失败、取消、超时、节点上限、回溯失败或最终复核失败均保留截至返回时的路径搜索耗时。所有结果满足:
TimeSpan.Zero <= PathSearchElapsed <= Elapsed
构造函数的新参数位于现有参数之后并具有 TimeSpan.Zero 默认值,保持现有调用兼容。
MovementTest 展示
状态图层显示:
- 地图状态、缓存命中和快照。
- 规划状态。
- 总耗时与路径搜索耗时。
- 扩展节点数、生成节点数和 Open List 峰值。
- 非空失败原因。
- 当前测试采用的车辆长、宽、安全余量和最小转弯半径。
Toast 保持单行摘要,但成功和失败都显示两种耗时;失败时必须附加 TerminationReason。这样即使用户没有看到地图左下角的状态图层,也不会只收到一个无原因的失败状态。
文档
CoarsePath/README.md 增加以下说明:
- 手动入口要求输入有限正数秒总预算。
- 总耗时与路径搜索耗时的不同边界。
- 手动入口采用固定演示车辆参数,不代表现场 AMR。
- 当前算法支持汽车式恒曲率前进、倒车和换向,但不支持 Reeds-Shepp 精确连接、横移、蟹行或原地旋转。
测试策略
所有行为修改遵循 Red-Green-Refactor。
输入与 UI 源码检查
verify_coarse_path_ui.ps1 先增加失败断言,再实现:
- 手动入口读取并应用超时秒数。
- 超时输入使用正数有限值校验。
- 状态图层和 Toast 都读取
PathSearchElapsed。 - Toast 读取
TerminationReason。 - 状态图层显示节点统计和车辆参数。
- README 包含新的耗时边界、手动预算和能力限制说明。
搜索与集成行为
verify_coarse_path_search.ps1 和 verify_coarse_path_integration.ps1 先增加失败断言,再实现:
- 超时、节点上限、无解和内部错误返回不同的非空原因。
InternalError原因包含异常类型或消息。- 搜索前失败的
PathSearchElapsed为零。 - 一个真实成功规划的路径搜索耗时非负且不超过总耗时。
- 搜索阶段失败保留已消耗的路径搜索耗时。
- 5 秒会超时而更长手动预算可成功的近距离换向案例得到回归覆盖;测试应使用足够稳定的预算余量,避免仅依赖精确墙钟阈值形成脆弱测试。
最终回归
最终运行 Debug 构建,以及现有地图、碰撞、搜索、集成和 UI 脚本。既有固定成功场景、取消、总超时、节点上限、无解、地图缓存和最终碰撞复核语义不得改变。
验收标准
- 手动“粗路径规划”每次要求输入有限正数秒总预算,并应用到该次请求。
- 失败 Toast 能直接区分超时、节点上限、无解、碰撞和内部错误,并包含可操作原因。
- 状态图层同时显示两种耗时、搜索统计和演示车辆参数。
- 可行但超过默认 5 秒的复现场景可以通过更长的用户输入预算成功。
- 所有新旧自动化验证通过。
- 算法第一版能力限制在 README 中可见,不再被误认为测试入口暗中禁用完整算法。