chore: save current workspace progress
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# TrapMap完整图片导出与终端日志开关设计
|
||||
|
||||
## 目标
|
||||
|
||||
为`MovementTest.Trapmaptest.cs`增加两个相互独立的测试开关:
|
||||
|
||||
- 成功建图后,将完整栅格地图独立渲染为300 DPI PNG,不依赖Clumsy当前视口、缩放或其他Painter。
|
||||
- 控制TrapMap调试信息是否同步打印到宿主进程终端,同时始终保留`DLog`日志。
|
||||
|
||||
现有`TrapMapTest` Painter初始化和清理已经使用世界坐标调用`UI.GetPainter("TrapMapTest")`,本次不重复修改。两腿检测ROI继续使用车体局部Painter及`false`参数。
|
||||
|
||||
## 用户开关与默认值
|
||||
|
||||
在`TrapMapTest`手动编辑区增加:
|
||||
|
||||
```csharp
|
||||
private const bool _saveFullMapImage = true;
|
||||
private const bool _enableTerminalDebugLog = true;
|
||||
```
|
||||
|
||||
两个值传入`TrapMapBuilder`或共享日志/导出组件。关闭图片开关时不得创建目录、临时文件或PNG。关闭终端开关时只抑制`Console.WriteLine`,不得抑制`DLog`和必要的UI提示。
|
||||
|
||||
## 图片内容
|
||||
|
||||
图片从最终发布的`GridMapData`离屏渲染,包含完整地图边界而不是屏幕截图:
|
||||
|
||||
- 白色背景。
|
||||
- 浅灰色完整栅格线,确保每个小格可见。
|
||||
- 红色占用栅格。
|
||||
- 蓝色车辆轮廓、几何中心和朝向。
|
||||
- 绿色工作站目标标记。
|
||||
- 黑色地图外边界。
|
||||
- 标题/图例区域,显示世界边界、分辨率、行列数、占据率、障碍物数量、轮胎层状态和输入来源。
|
||||
|
||||
世界X轴在图片中向右;世界Y轴向上,因此从`Cells[row,col]`映射到位图时反转图像Y方向。工作站仅绘制标记,不写入占用数据。
|
||||
|
||||
## 图片尺寸与文件约束
|
||||
|
||||
- 每个栅格使用`4×4`像素。
|
||||
- PNG水平和垂直DPI都设置为`300`。
|
||||
- 包含边距和标题后,任一图片边长不得超过`4000`像素。
|
||||
- 最终PNG文件大小不得超过`50 * 1024 * 1024`字节。
|
||||
|
||||
尺寸在分配RGBA像素缓冲区前检查。若超过4000像素,跳过导出并报告明确原因。编码先写入同目录临时文件,完成后检查实际字节数;超过50MB时删除临时文件,不留下超限最终文件。只有所有检查通过后,才原子移动/重命名为最终PNG。
|
||||
|
||||
典型`327×139`地图的栅格主体约为`1308×556`像素,另加标题和边距。
|
||||
|
||||
## 保存位置与命名
|
||||
|
||||
输出根目录使用宿主进程当前工作目录:
|
||||
|
||||
```text
|
||||
TrapMapExports\TrapMap_yyyyMMdd_HHmmss_fff.png
|
||||
```
|
||||
|
||||
毫秒时间戳避免同一秒多次测试覆盖。目录只在图片开关打开且地图成功后创建。临时文件使用同目录、同文件名加`.tmp`后缀,以保证最终重命名不跨磁盘。
|
||||
|
||||
## 日志行为
|
||||
|
||||
引入TrapMap专用日志入口,其行为为:
|
||||
|
||||
```text
|
||||
所有消息 -> DLog.Log(message, "TrapMapTest")
|
||||
终端开关开启 -> 额外Console.WriteLine("[TrapMapTest] " + message)
|
||||
```
|
||||
|
||||
至少覆盖测试开始、输入参数、Detour位姿、车辆尺寸、地图边界/尺寸、轮胎层状态、图片保存成功/跳过/失败、最终统计和测试停止。图片错误不得因终端开关关闭而静默,仍必须进入`DLog`。
|
||||
|
||||
## 组件边界
|
||||
|
||||
图片导出放在独立文件`ClumsyPilot/TrapMapImageExporter.cs`,避免继续扩大已经较长的MovementTest文件。组件只消费不可变的导出请求数据:地图、车辆位姿、工作站、轮胎层元数据和目标文件路径;它不读取Detour、雷达或UI,也不修改栅格。
|
||||
|
||||
`MovementTest.Trapmaptest.cs`负责开关、调用时机、日志和错误降级。导出发生在地图成功生成之后;导出失败不改变`TrapMapBuilder.Succeeded`或`GridMap`。
|
||||
|
||||
实现使用内部纯C# RGBA光栅器绘制栅格、车辆、工作站和5×7位图文字,再由精确版本`StbImageWriteSharp` 1.16.7编码PNG。编码后立即在`IHDR`后插入`pHYs=11811/11811/unit1`,以保留300 DPI元数据。运行时不依赖平台绘图程序集或原生图形资产;除单个托管Stb编码程序集外,光栅、元数据和文件流程均只使用BCL。
|
||||
|
||||
## 错误处理
|
||||
|
||||
以下情况只导致图片导出失败,不导致建图失败:
|
||||
|
||||
- 图片开关关闭。
|
||||
- 图片尺寸超过4000像素。
|
||||
- 输出目录创建失败。
|
||||
- RGBA缓冲区创建、绘制或PNG编码异常。
|
||||
- 临时文件超过50MB。
|
||||
- 临时文件重命名失败。
|
||||
|
||||
异常路径必须尽力删除本次临时文件,不得删除已有的成功PNG。
|
||||
|
||||
## 验证要求
|
||||
|
||||
至少验证:
|
||||
|
||||
1. 图片开关关闭时不创建文件和目录。
|
||||
2. 小型已知栅格导出的PNG由BCL测试解码器重新读取;所有chunk CRC有效,`IHDR`为RGBA8,`pHYs`表示300 DPI,像素尺寸符合4像素/格及布局规则。
|
||||
3. PNG中占用格、车辆和工作站采样位置颜色正确,Y轴没有上下颠倒。
|
||||
4. 超过4000像素的请求在RGBA缓冲区分配前失败。
|
||||
5. 最终路径使用毫秒时间戳且不覆盖旧文件。
|
||||
6. 成功文件严格小于或等于50MB,超限临时文件被删除。
|
||||
7. 终端开关开启时消息同时进入DLog和终端;关闭时仍进入DLog但不写终端。
|
||||
8. 图片失败时地图仍为成功状态。
|
||||
9. 现有源码契约、栅格行为、生命周期测试及`ClumsyPilot`编译继续通过。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不截取Clumsy/CycleGUI窗口。
|
||||
- 不保存紫色UI背景、小车3D模型、绿色两腿ROI或橙色检测猜测线。
|
||||
- 不改变栅格数据格式或地图边界计算。
|
||||
- 不接入新的点云来源。
|
||||
- 不修改`TrajPlanner`。
|
||||
- 不提交或暂存本次工作区改动。
|
||||
@@ -0,0 +1,52 @@
|
||||
# TrapMap Managed PNG Export Design
|
||||
|
||||
## Goal
|
||||
|
||||
Replace `System.Drawing.Common` in TrapMap image export so Clumsy can save the full grid PNG without depending on platform-specific drawing assemblies.
|
||||
|
||||
## Chosen approach
|
||||
|
||||
Use a small internal RGBA rasterizer for grid primitives and `StbImageWriteSharp` 1.16.7 for PNG encoding. Keep the public request/result API and the existing TrapMap call site unchanged.
|
||||
|
||||
Alternatives rejected:
|
||||
|
||||
- Copying `System.Drawing.Common.dll` beside the build output is unreliable because Clumsy performs its own dependency attachment and can load the incompatible `netstandard2.0` facade first.
|
||||
- `SkiaSharp` adds native Windows assets and more deployment points.
|
||||
- `ImageSharp` has a larger dependency/licensing surface and its current release does not target this project's `netstandard2.0` runtime.
|
||||
|
||||
`StbImageWriteSharp` is selected because its package contains one managed `netstandard2.0` implementation and declares no dependencies. It has no native assets or alternate platform facades for Clumsy to select incorrectly.
|
||||
|
||||
## Rasterization
|
||||
|
||||
- Allocate an in-memory 32-bit RGBA pixel buffer after the existing 4000-pixel edge validation.
|
||||
- Preserve the current canvas dimensions, 4 pixels per cell, colors, Y inversion, vehicle/workstation geometry, header height, and 50 MiB final-file limit.
|
||||
- Draw filled rectangles, 1/2-pixel lines, circles, crosses, and vehicle polygons with deterministic integer raster operations.
|
||||
- Render header and workstation text with a small embedded ASCII bitmap font. Non-ASCII metadata is sanitized to a printable fallback for the PNG header only; original messages remain unchanged in DLog/Console.
|
||||
- Keep header lines non-overlapping and include bounds, resolution, rows/columns, occupied count/rate, obstacle count, tire status/message, and input source.
|
||||
|
||||
## PNG encoding
|
||||
|
||||
- Pass the RGBA pixel buffer to `StbImageWriteSharp.ImageWriter.WritePng`.
|
||||
- Insert a standard `pHYs` chunk immediately after `IHDR`, with X/Y both 11,811 pixels per metre and unit `1` (300 DPI).
|
||||
- Calculate the inserted chunk's CRC-32 and write its integers in big-endian order.
|
||||
- Validate the completed PNG before publication; encoding or metadata failures remain contained export failures.
|
||||
- Preserve the current collision-safe temporary-file reservation, actual encoded-size check, atomic move, and best-effort cleanup behavior.
|
||||
|
||||
## Project and deployment changes
|
||||
|
||||
- Remove the `System.Drawing.Common` package reference and `DeployFrameworkDrawingRuntime` target from `ClumsyPilot.csproj`.
|
||||
- Add `StbImageWriteSharp` version 1.16.7. No other new package is allowed.
|
||||
- The final build output must not require or deploy `System.Drawing.Common.dll` for TrapMap; it may deploy the single managed `StbImageWriteSharp.dll`.
|
||||
- Existing Clumsy references are not modified.
|
||||
|
||||
## Verification
|
||||
|
||||
- TDD first proves the current exporter/package still depends on `System.Drawing.Common`.
|
||||
- Reflection/source contracts assert the old package, build target, `using System.Drawing`, and drawing types are absent, and the exact Stb package is present.
|
||||
- Decode the generated PNG in the test with a test-only decoder or framework reader and verify dimensions, 300 DPI metadata, representative colors, Y inversion, header separation, unique concurrent filenames, and no temporary files.
|
||||
- Run source, build, grid, lifecycle, and image tests; require zero build errors and no new warnings.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No changes to grid construction, obstacle/tire inputs, vehicle motion, Painter visualization, export switches, output directory, or file-size/pixel limits.
|
||||
- No screenshot capture and no new general-purpose graphics framework.
|
||||
@@ -4,92 +4,126 @@
|
||||
|
||||
在现有栅格地图能力之上,实现四舵轮 AMR 的 Hybrid A* 空间粗路径:支持前进、倒车、换向、静态/投影障碍绕行、车体碰撞检查、可配置的终点位置与航向容差,以及稠密路径与方向分段输出。
|
||||
|
||||
本阶段**不包含**曲线平滑、B 样条、Bezier、局部 QP、SQP、Reeds-Shepp 精确终点连接、速度/加速度/时间参数化或底盘舵角控制。它们由后续独立模块处理。
|
||||
本阶段**不包含**曲线平滑、B 样条、Bezier、局部 QP、SQP、Reeds-Shepp 精确终点连接、速度/加速度/时间参数化或底盘舵角控制。第一版采用汽车式恒曲率模型,不支持蟹行、纯横移和原地旋转。它们由后续独立模块处理。
|
||||
|
||||
## 已确认的约束
|
||||
|
||||
- 地图与外部人工障碍输入继续使用现有世界坐标单位:mm。
|
||||
- Hybrid A* 内部统一使用 m、rad、1/m;单位转换只能经过地图适配与 `Utils`,不能散落在搜索代码中。
|
||||
- 人工障碍物第一版支持以几何中心放置的轴对齐矩形和圆形。
|
||||
- `TwoLegDetect` 是可选障碍物输入,不是规划地图是否可用的唯一条件;它的检测结果经投影后与人工障碍物合并。
|
||||
- `TwoLegProjectionInput` 是可选障碍物输入,不是规划地图是否可用的唯一条件;上层采集到的 `TwoLegDetect` 结果经 DTO 投影后与人工障碍物合并。
|
||||
- 环境占据地图只保存外部障碍物,绝不写入 AMR 自身足迹。AMR 尺寸、当前位姿和安全余量只用于粗路径的车体碰撞检测。
|
||||
- 障碍物不做安全膨胀;安全余量仅通过碰撞检查时的扩大车辆矩形应用,防止双重膨胀。
|
||||
- 第一版的终点条件为可配置的位置容差和车头航向容差。默认建议为 0.15 m 与 5°,而非精确连接到目标位姿。
|
||||
- `PrimitiveLength=0.50 m` 是单条原语的最大长度,不是终点只能出现的离散间隔。每条原语必须在内部积分点检查目标条件;该原语内首次满足条件时立即截断并建立终点候选节点。终点候选仍须进入 Open List,只有当它作为当前最优有效节点出队时,整个搜索才返回成功。
|
||||
- 碰撞检测必须保守:不得因航向离散、车辆中心的亚栅格偏移、距离场误差或原语离散采样而发布可能碰撞的路径。
|
||||
- `ObstacleDistanceField` 只能作为保守快速放行和代价估计,任何可能高估真实净空的近似都不得用于跳过精确车体碰撞检查。
|
||||
- 测试是交付的一部分:纯逻辑契约测试与参照 `Map` 的 `MovementTest` 集成/可视化入口都必须提供。
|
||||
- 每个文件只承担一个明确职责;对外调用通过模块门面类完成,不让调用者拼装搜索、地图和碰撞的内部对象。
|
||||
- 推荐调用方只使用 `CoarsePathPlanningService` 一次完成建图、粗路径搜索和可选调试发布;`PlanningMapFactory` 与 `HybridAStarPlanner` 是可独立测试、复用的下层模块门面。请求、结果、枚举、障碍物 DTO 和值对象仍然是可公开构造的数据契约。
|
||||
|
||||
## 目录和命名空间
|
||||
|
||||
```text
|
||||
ClumsyPilot/ParkrobTrajplanner/
|
||||
├── Initial_plan/ # 已有路线与方案文档,不放运行时代码
|
||||
├── Utils/ # MultiWheelC.TrajectoryPlanning.Utils
|
||||
│ ├── AngleMath.cs
|
||||
│ ├── UnitConverter.cs
|
||||
│ ├── CoordinateTransform.cs
|
||||
│ ├── NumericGuard.cs
|
||||
│ └── GridIndex.cs
|
||||
├── Map/ # MultiWheelC.TrajectoryPlanning.Mapping
|
||||
├── Initial_plan/ ------ 已有路线与方案文档,不放运行时代码
|
||||
├── Utils/ ------ 命名空间 MultiWheelC.TrajectoryPlanning.Utils
|
||||
│ ├── AngleMath.cs ------ 角度归一化、最短角差和航向离散索引
|
||||
│ ├── UnitConverter.cs ------ mm/m、deg/rad 和半径/曲率单位转换
|
||||
│ ├── CoordinateTransform.cs ------ 车体坐标系与世界坐标系二维刚体变换
|
||||
│ ├── NumericGuard.cs ------ 有限值、正值和参数范围校验
|
||||
│ └── GridIndex.cs ------ 不可变行列索引值对象
|
||||
├── Map/ ------ 命名空间 MultiWheelC.TrajectoryPlanning.Mapping
|
||||
│ ├── Core/
|
||||
│ │ ├── EnvironmentGridMap.cs
|
||||
│ │ ├── MapBuildRequest.cs
|
||||
│ │ ├── EnvironmentMapBuildResult.cs
|
||||
│ │ └── EnvironmentMapBuilder.cs
|
||||
│ │ ├── EnvironmentGridMap.cs ------ 只保存外部障碍物的 mm 单位占据栅格
|
||||
│ │ ├── MapBoundsMm.cs ------ 有限、非退化的 mm 地图边界值对象
|
||||
│ │ ├── MapBuildRequest.cs ------ 环境地图边界、分辨率和障碍物图层输入
|
||||
│ │ ├── EnvironmentMapBuildResult.cs ------ 环境地图构建状态、来源摘要和失败原因
|
||||
│ │ └── EnvironmentMapBuilder.cs ------ 校验并合并人工与投影障碍物图层
|
||||
│ ├── Obstacles/
|
||||
│ │ ├── IMapObstacle.cs
|
||||
│ │ ├── AxisAlignedRectangleObstacle.cs
|
||||
│ │ ├── CircleObstacle.cs
|
||||
│ │ └── MapObstacleRasterizer.cs
|
||||
│ │ ├── IMapObstacle.cs ------ 人工和投影障碍物的公共几何契约
|
||||
│ │ ├── AxisAlignedRectangleObstacle.cs ------ 世界轴对齐矩形障碍物 DTO
|
||||
│ │ ├── CircleObstacle.cs ------ 世界坐标圆形障碍物 DTO
|
||||
│ │ └── MapObstacleRasterizer.cs ------ 通过形状与格子相交测试写入占据栅格
|
||||
│ ├── Sources/
|
||||
│ │ ├── ManualObstacleSource.cs
|
||||
│ │ └── TwoLegObstacleProjector.cs
|
||||
│ │ ├── IMapObstacleSource.cs ------ 纯快照障碍来源统一投影接口
|
||||
│ │ ├── ObstacleSourceStatus.cs ------ Applied、Empty、Unavailable、Invalid 来源状态
|
||||
│ │ ├── ObstacleProjectionResult.cs ------ 来源版本、状态、诊断和世界障碍物集合
|
||||
│ │ ├── ManualObstacleSource.cs ------ 把人工圆和矩形作为世界障碍物输出
|
||||
│ │ ├── TwoLegProjectionInput.cs ------ 已验证两腿端点、检测状态和检测时车辆位姿 DTO
|
||||
│ │ ├── TwoLegObstacleSource.cs ------ 把 TwoLeg 快照接入统一障碍来源接口
|
||||
│ │ └── TwoLegObstacleProjector.cs ------ 把车体系两腿端点投影为世界坐标圆障碍
|
||||
│ ├── Planning/
|
||||
│ │ ├── PlanningGridMap.cs
|
||||
│ │ ├── ObstacleDistanceField.cs
|
||||
│ │ └── PlanningMapAdapter.cs
|
||||
│ ├── PlanningMapRequest.cs
|
||||
│ ├── PlanningMapBuildResult.cs
|
||||
│ └── PlanningMapFactory.cs
|
||||
│ │ ├── PlanningGridMap.cs ------ 只读 m 单位占据图、距离场和规划可用状态
|
||||
│ │ ├── EuclideanDistanceTransform.cs ------ 线性时间生成栅格中心精确欧氏距离
|
||||
│ │ ├── ObstacleDistanceField.cs ------ 生成不高估障碍净空的保守欧氏距离下界
|
||||
│ │ ├── PlanningMapCache.cs ------ 线程安全的输入指纹与占据哈希快照缓存
|
||||
│ │ └── PlanningMapAdapter.cs ------ 深拷贝占据数据并完成 mm 到 m 的边界适配
|
||||
│ ├── PlanningMapRequest.cs ------ 地图门面的统一输入契约
|
||||
│ ├── PlanningMapBuildResult.cs ------ 地图门面的统一输出契约
|
||||
│ ├── PlanningMapFactory.cs ------ Map 模块唯一公共行为入口
|
||||
│ └── Test/
|
||||
│ └── MovementTest.MapTest.cs
|
||||
├── CoarsePath/ # MultiWheelC.TrajectoryPlanning.CoarsePath
|
||||
│ ├── MovementTest.MapTest.cs ------ Clumsy UI 地图构建与可视化测试入口
|
||||
│ └── Visualization/
|
||||
│ ├── PlanningMapImageExportRequest.cs ------ 规划快照、叠加层和输出选项 DTO
|
||||
│ ├── PlanningMapImageExportResult.cs ------ PNG 导出状态、路径、大小和诊断
|
||||
│ ├── PlanningMapImageExporter.cs ------ 校验请求、编排渲染并原子发布 PNG
|
||||
│ ├── PlanningMapImageRenderer.cs ------ 把地图、起终点、车体和路径绘制到 RGBA
|
||||
│ └── ValidatedPngWriter.cs ------ Stb PNG 编码、结构和 CRC 完整性校验
|
||||
├── CoarsePath/ ------ 命名空间 MultiWheelC.TrajectoryPlanning.CoarsePath
|
||||
│ ├── Contracts/
|
||||
│ │ ├── Pose2D.cs
|
||||
│ │ ├── VehicleParameters.cs
|
||||
│ │ ├── PlanningRequest.cs
|
||||
│ │ ├── HybridAStarConfiguration.cs
|
||||
│ │ ├── PlanningResult.cs
|
||||
│ │ ├── PlanningStatus.cs
|
||||
│ │ ├── CoarsePathPoint.cs
|
||||
│ │ └── PathSegment.cs
|
||||
│ │ ├── Pose2D.cs ------ m/rad 单位的不可变二维位姿
|
||||
│ │ ├── TravelDirection.cs ------ Forward 与 Reverse 运动方向枚举
|
||||
│ │ ├── GoalDirectionConstraint.cs ------ Any、Forward、Reverse 目标进入方向约束
|
||||
│ │ ├── VehicleParameters.cs ------ 车体尺寸、安全余量和最大曲率参数
|
||||
│ │ ├── PlanningRequest.cs ------ 地图、起终点、起始曲率和方向约束
|
||||
│ │ ├── HybridAStarConfiguration.cs ------ 原语、离散、代价、限额和容差配置
|
||||
│ │ ├── PlanningResult.cs ------ 状态、诊断、稠密路径和方向分段
|
||||
│ │ ├── PlanningStatus.cs ------ 输入、碰撞、搜索和验证结果枚举
|
||||
│ │ ├── PlanningDiagnostics.cs ------ 节点、堆、耗时、路径和终止统计
|
||||
│ │ ├── CoarsePathPoint.cs ------ 位姿、弧长、方向、曲率和保守净空
|
||||
│ │ ├── CoarsePathPointSource.cs ------ 起点、普通原语和终点截断来源枚举
|
||||
│ │ └── PathSegment.cs ------ 前进/倒车分段及其包含式索引范围
|
||||
│ ├── Vehicle/
|
||||
│ │ ├── VehicleKinematics.cs
|
||||
│ │ ├── HeadingFootprintTemplate.cs
|
||||
│ │ ├── HeadingFootprintTemplateCache.cs
|
||||
│ │ └── FootprintCollisionChecker.cs
|
||||
│ │ ├── VehicleKinematics.cs ------ 解析并校验车辆保守最大曲率
|
||||
│ │ ├── VehicleFootprint.cs ------ 计算扩大车体矩形、包围盒和外接圆
|
||||
│ │ ├── OrientedRectangleCellIntersection.cs ------ 精确判断旋转车体矩形与栅格矩形相交
|
||||
│ │ └── FootprintCollisionChecker.cs ------ 边界、距离场、精确和扫掠碰撞检查
|
||||
│ ├── Search/
|
||||
│ │ ├── MotionPrimitive.cs
|
||||
│ │ ├── MotionPrimitiveGenerator.cs
|
||||
│ │ ├── HybridAStarNode.cs
|
||||
│ │ ├── HybridAStarNodeKey.cs
|
||||
│ │ ├── GoalToleranceChecker.cs
|
||||
│ │ ├── GridDijkstraHeuristic.cs
|
||||
│ │ └── HybridAStarSearch.cs
|
||||
│ │ ├── MotionPrimitive.cs ------ 单条恒曲率原语及其实际截断长度描述
|
||||
│ │ ├── MotionPrimitiveGenerator.cs ------ 解析积分前进/倒车原语并保留内部点
|
||||
│ │ ├── BinaryMinHeap.cs ------ netstandard2.0 兼容且确定性排序的 Open List
|
||||
│ │ ├── SearchCostCalculator.cs ------ 统一计算长度、倒车、换向、曲率和净空代价
|
||||
│ │ ├── HybridAStarNode.cs ------ 连续位姿、代价、父索引和原语描述
|
||||
│ │ ├── HybridAStarNodeKey.cs ------ 位置格、航向格、方向和曲率离散键
|
||||
│ │ ├── GoalToleranceChecker.cs ------ 位置、航向和目标进入方向容差判断
|
||||
│ │ ├── GridDijkstraHeuristic.cs ------ 八邻域二维绕障距离启发
|
||||
│ │ └── HybridAStarSearch.cs ------ 节点扩展、重开、限额和终点候选管理
|
||||
│ ├── Output/
|
||||
│ │ ├── PathBacktracker.cs
|
||||
│ │ ├── CoarsePathAssembler.cs
|
||||
│ │ └── CoarsePathValidator.cs
|
||||
│ ├── HybridAStarPlanner.cs
|
||||
│ │ ├── PathBacktracker.cs ------ 按父索引确定性重建原语内部点
|
||||
│ │ ├── CoarsePathAssembler.cs ------ 生成弧长、换向点和包含式方向分段
|
||||
│ │ └── CoarsePathValidator.cs ------ 复核数值、碰撞、曲率、终点和分段
|
||||
│ ├── HybridAStarPlanner.cs ------ 只消费 PlanningGridMap 的纯搜索下层门面
|
||||
│ ├── Facade/
|
||||
│ │ ├── CoarsePathPlanningJob.cs ------ 一次调用所需地图、起终点、车辆和调试选项
|
||||
│ │ ├── CoarsePathPlanningJobResult.cs ------ 同时返回地图构建结果和粗路径结果
|
||||
│ │ ├── PlanningDebugOptions.cs ------ 地图、路径和碰撞调试发布开关
|
||||
│ │ ├── IPlanningDebugSink.cs ------ 不影响规划状态的调试结果消费接口
|
||||
│ │ └── CoarsePathPlanningService.cs ------ 建图、缓存、搜索和调试编排的一次调用入口
|
||||
│ └── Test/
|
||||
│ ├── CoarsePathScenarioFactory.cs
|
||||
│ └── MovementTest.CoarsePathTest.cs
|
||||
└── README.md # 只说明模块边界、调用入口与文档链接
|
||||
│ ├── CoarsePathScenarioFactory.cs ------ 生成固定、可复现的地图和规划场景
|
||||
│ └── MovementTest.CoarsePathTest.cs ------ 后台运行规划并在 Clumsy UI 绘制结果
|
||||
│ └── README.md ------ 粗规划模块边界、公共调用入口和文档链接
|
||||
|
||||
ClumsyPilot/tests/
|
||||
├── verify_planning_utils.ps1
|
||||
├── verify_planning_map_adapter.ps1
|
||||
├── verify_coarse_path_search.ps1
|
||||
└── verify_coarse_path_integration.ps1
|
||||
├── verify_planning_utils.ps1 ------ 单位、角度、坐标和数值守卫测试
|
||||
├── verify_planning_map_factory.ps1 ------ 地图输入、图层事务、快照和版本测试
|
||||
├── verify_planning_map_adapter.ps1 ------ 地图栅格化、图层、适配和距离场测试
|
||||
├── verify_planning_map_image.ps1 ------ 只读快照 PNG 渲染、限制和原子发布测试
|
||||
├── verify_coarse_path_collision.ps1 ------ 亚栅格、擦边和扫掠碰撞测试
|
||||
├── verify_coarse_path_search.ps1 ------ 原语截断、搜索、方向、限额和重开测试
|
||||
├── verify_coarse_path_integration.ps1 ------ Map 到最终路径验证的端到端测试
|
||||
└── benchmark_coarse_path.ps1 ------ 参考与压力场景性能资源验收
|
||||
```
|
||||
|
||||
`Occupancygird_Map/Map_test` 是当前原型位置。实施时会把其中仍然需要的地图能力按以上职责迁移到 `Map`,避免继续向两个现有大文件叠加功能;图片导出可以保留为独立地图测试辅助,不成为规划器依赖。
|
||||
@@ -110,7 +144,7 @@ ClumsyPilot/tests/
|
||||
|
||||
### 对外调用门面
|
||||
|
||||
粗路径模块不直接实例化 `EnvironmentMapBuilder`、`MapObstacleRasterizer`、`TwoLegObstacleProjector` 或 `PlanningMapAdapter`。`PlanningMapFactory` 是 `Map` 模块唯一的公共创建入口:
|
||||
`HybridAStarPlanner` 不直接实例化任何地图对象;`CoarsePathPlanningService` 只持有 `PlanningMapFactory`,不接触 `EnvironmentMapBuilder`、`MapObstacleRasterizer`、来源投影器或 `PlanningMapAdapter`。`PlanningMapFactory` 是 `Map` 模块唯一的公共创建入口:
|
||||
|
||||
```csharp
|
||||
public sealed class PlanningMapFactory
|
||||
@@ -119,7 +153,7 @@ public sealed class PlanningMapFactory
|
||||
}
|
||||
```
|
||||
|
||||
粗路径调用只依赖其稳定输出:
|
||||
下层模块独立调用时只依赖其稳定输出:
|
||||
|
||||
```csharp
|
||||
var mapResult = new PlanningMapFactory().Create(mapRequest);
|
||||
@@ -136,13 +170,44 @@ var result = new HybridAStarPlanner().Plan(new PlanningRequest
|
||||
});
|
||||
```
|
||||
|
||||
`PlanningMapRequest` 集中地图边界/分辨率、人工障碍物、可选 `TwoLegDetect` 投影输入和空旷地图声明;`PlanningMapBuildResult` 返回 `Succeeded`、失败原因、每个障碍物来源的摘要,以及成功时不可变的 `PlanningGridMap`。因此 A* 的 `PlanningRequest.Map` 始终是已完成校验、mm→m 适配、距离场生成的输入,规划器不需要了解地图构造细节。
|
||||
`PlanningMapRequest` 集中地图边界/分辨率、`IReadOnlyList<IMapObstacleSource>` 和空旷地图声明;`PlanningMapBuildResult` 返回 `Succeeded`、失败原因、每个障碍物来源的摘要、缓存命中类型,以及成功时不可变的 `PlanningGridMap`。因此 A* 的 `PlanningRequest.Map` 始终是已完成校验、投影、栅格化、mm→m 适配和距离场生成的输入,规划器不需要了解地图构造细节。
|
||||
|
||||
### 统一障碍物来源与投影
|
||||
|
||||
所有人工、TwoLeg 和后续障碍物输入统一实现:
|
||||
|
||||
```csharp
|
||||
public interface IMapObstacleSource
|
||||
{
|
||||
string SourceId { get; }
|
||||
long SourceVersion { get; }
|
||||
bool IsRequired { get; }
|
||||
ObstacleProjectionResult ProjectToWorld();
|
||||
}
|
||||
```
|
||||
|
||||
`ProjectToWorld` 只能消费构造来源对象时已经取得的不可变快照,不得在内部读取传感器、定位、UI 或系统时间。它统一返回世界坐标 mm 几何体:
|
||||
|
||||
```text
|
||||
ObstacleProjectionResult:
|
||||
SourceId
|
||||
SourceVersion
|
||||
Status Applied/Empty/Unavailable/Invalid
|
||||
Message
|
||||
IReadOnlyList<IMapObstacle> Obstacles
|
||||
```
|
||||
|
||||
必需来源返回 `Unavailable/Invalid` 时地图构建失败;可选来源返回上述状态时记录诊断并继续处理其他来源。多个来源产生重叠障碍物是合法的,占据写入具有幂等语义。
|
||||
|
||||
`ManualObstacleSource` 直接输出已经位于世界坐标系的圆和轴对齐矩形。`TwoLegProjectionInput` 是纯数据 DTO,明确包含检测状态、车体坐标系中的两个端点、端点半径、检测时刻的 AMR 世界位姿,以及 mm/deg 单位声明。`MovementTest` 或上层采集适配器负责调用现有 `TwoLegDetect`,随后把结果封装为快照;`TwoLegObstacleSource` 委托 `TwoLegObstacleProjector` 执行确定性车体到世界变换并输出零个或两个 `CircleObstacle`。
|
||||
|
||||
新增障碍物来源只需投影为现有 `IMapObstacle` 几何体;如果需要新增多边形等几何类型,必须同时为 `MapObstacleRasterizer` 添加保守的形状-栅格相交实现和成功/边界/失败测试。任何来源都不得应用车辆安全余量。
|
||||
|
||||
### 环境地图与图层
|
||||
|
||||
`EnvironmentGridMap` 保存 mm 单位的地图边界、分辨率和仅含外部障碍物的占据单元。它不接受 `MarkVehicleFootprint` 一类接口。
|
||||
|
||||
`EnvironmentMapBuilder` 是地图模块的唯一对外构造入口:
|
||||
`EnvironmentMapBuilder` 是由 `PlanningMapFactory` 使用的内部环境图构造器:
|
||||
|
||||
```csharp
|
||||
public sealed class EnvironmentMapBuilder
|
||||
@@ -151,11 +216,11 @@ public sealed class EnvironmentMapBuilder
|
||||
}
|
||||
```
|
||||
|
||||
它依次校验地图参数、栅格化人工障碍物、投影可用的 `TwoLegDetect` 结果、合并占据单元,并报告每个来源是否生效。任何可选传感器输入失败都不会删除已成功构建的人工地图。
|
||||
它依次校验地图参数、按 `SourceId` 确定性排序来源、收集 `ObstacleProjectionResult`、合并所有成功投影的 `IMapObstacle` 并调用唯一栅格化器。任何可选来源失败都不会删除其他来源已成功构建的占据内容。
|
||||
|
||||
人工障碍物实现 `IMapObstacle`。`AxisAlignedRectangleObstacle` 使用 `(CenterXmm, CenterYmm, WidthMm, HeightMm)`;`CircleObstacle` 使用 `(CenterXmm, CenterYmm, RadiusMm)`。两者的尺寸必须为有限正数,矩形轴与世界 X/Y 轴对齐。`MapObstacleRasterizer` 是唯一直接写入环境栅格的类。
|
||||
|
||||
`TwoLegObstacleProjector` 仅负责将检测坐标通过现有车体到世界系变换变成 `CircleObstacle`,不负责地图边界、栅格化或规划可用性判断。
|
||||
`MapObstacleRasterizer` 是唯一直接写入 `EnvironmentGridMap` 的类型;各来源和投影器都不能取得地图写入接口。`TwoLegObstacleProjector` 不负责地图边界、栅格化、缓存或规划可用性判断。
|
||||
|
||||
### 规划适配
|
||||
|
||||
@@ -168,47 +233,268 @@ public sealed class PlanningMapAdapter
|
||||
}
|
||||
```
|
||||
|
||||
适配时验证边界和分辨率、深拷贝占据数据、将长度从 mm 转为 m、将边界外固定解释为占据,并生成 `ObstacleDistanceField`。`PlanningGridMap` 是不可变的规划输入,包含 m 单位边界、分辨率、行列、占据数据、距离场、源地图版本与 `PlanningReady/PlanningBlockReason`。
|
||||
适配时验证边界和分辨率、深拷贝占据数据、将长度从 mm 转为 m、将边界外固定解释为占据,并生成 `ObstacleDistanceField`。`PlanningGridMap` 是不可变的规划输入,包含 m 单位边界、分辨率、行列、占据数据、距离场、来源版本摘要、快照标识与 `PlanningReady/PlanningBlockReason`。
|
||||
|
||||
`PlanningReady` 的规则:有效的人工图层即可使地图可用于测试/规划;若没有人工障碍也没有 `TwoLegDetect`,在已明确配置“空旷地图”时仍可规划;未明确空旷语义的未观测区域则以 `PlanningBlockReason` 阻止规划,而不是暗中当作空闲。
|
||||
距离场使用精确的二维欧氏距离变换计算栅格中心到最近占据栅格中心的距离,再减去一个完整栅格对角线 `sqrt(2) * ResolutionMeters` 并截断到零,得到当前自由栅格内任意点到任意占据栅格矩形的保守下界。查询不得进行会抬高结果的插值;查询点使用其所在栅格的保守值。显式空旷地图的障碍物距离可以是正无穷,但车体边界检查仍必须先执行,地图外始终按占据处理。
|
||||
|
||||
距离场的用途受以下规则约束:
|
||||
|
||||
- 当保守距离严格大于扩大车体外接圆半径时,碰撞检查器可以快速放行。
|
||||
- 当保守距离小于或等于外接圆半径时,必须执行精确的扩大车体矩形与占据栅格矩形相交检查。
|
||||
- `BodyClearance` 发布 `max(0, ConservativeCenterClearance - ExpandedFootprintCircumscribedRadius)`,作为车体净空的保守下界,不得声称为精确几何净空。
|
||||
- 多源距离场实现、空图语义、地图边界和最大栅格数都必须有自动化测试。
|
||||
|
||||
`PlanningReady` 的规则:至少一个成功来源提供有效障碍语义即可使地图用于测试/规划;所有来源均为空时,只有已明确配置 `AllowExplicitEmptyMap=true` 才可规划。必需来源失败或未明确空旷语义时,以 `PlanningBlockReason` 阻止规划,而不是暗中把未观测区域当作空闲。
|
||||
|
||||
### 现有地图优化与规划适配
|
||||
|
||||
本阶段不在旧 `GridMapData` 外再包一层长期兼容适配器,而是把其中经过测试的几何规则迁移为纯逻辑、静态快照式地图管线。迁移目标是消除 UI、传感器、车辆自身足迹和规划查询之间的职责耦合,同时降低 Hybrid A* 高频占据查询与距离查询的开销。
|
||||
|
||||
#### 现有职责拆分
|
||||
|
||||
| 现有类型/函数 | 处理方式 | 新职责位置 |
|
||||
| --- | --- | --- |
|
||||
| `TrapMapBounds.TryCreate` | 保留有限值、退化边界、分辨率和最大栅格数校验;移除“车辆到工作站”业务假设 | `MapBoundsMm`、`MapBuildRequest` |
|
||||
| `GridMapData.WorldToGrid/GridToWorld` | 保留 X→列、Y→行和 XMax/YMax 排他规则;统一处理最后一个非完整栅格 | `EnvironmentGridMap`、`PlanningGridMap` |
|
||||
| `GridMapData.Cells byte[,]` | 改为私有行优先 `byte[]`,索引固定为 `row * Cols + col`;不暴露可写数组 | 两类 GridMap 的内部存储 |
|
||||
| `GridMapData.MarkObstacle/MarkObstacles` | 移除默认安全距离;保留候选包围盒裁剪和形状-格矩形相交 | `MapObstacleRasterizer` |
|
||||
| `GridMapData.MarkVehicleFootprint` | 从地图模块删除,不提供兼容开关 | `FootprintCollisionChecker` 查询时处理 |
|
||||
| `GridMapData.Copy` | 不再暴露可变副本;适配时只进行一次占据缓冲区深拷贝 | `PlanningMapAdapter` |
|
||||
| `TrapMapLayerComposer.Compose` | 泛化为“可选来源失败不破坏其他成功来源”的事务语义 | `IMapObstacleSource`、`EnvironmentMapBuilder` |
|
||||
| `TrapMapBuilder.Get` | 拆除 `MovementDefinition`、定位读取、TwoLeg 调用、Toast、Painter 和协程依赖 | 上层采集适配器 + `CoarsePathPlanningService` |
|
||||
| `TrapMapImageExporter.cs` | 保留经过验证的纯托管 RGBA/PNG 能力,拆分请求、结果、渲染、发布和 PNG 校验职责;输入改为只读规划快照 | `Map/Test/Visualization` |
|
||||
|
||||
#### 统一地图输入与静态快照
|
||||
|
||||
`PlanningMapRequest` 必须显式包含:
|
||||
|
||||
```text
|
||||
MapBoundsMm Bounds
|
||||
float ResolutionMm
|
||||
IReadOnlyList<IMapObstacleSource> ObstacleSources
|
||||
bool AllowExplicitEmptyMap
|
||||
```
|
||||
|
||||
`Bounds` 使用 `[XMin, XMax) × [YMin, YMax)`;`ResolutionMm` 必须是 20~200 mm 的有限正数。来源 `SourceId` 必须非空且在一次请求内唯一,`SourceVersion` 必须非负,并在对应来源快照内容变化时递增。
|
||||
|
||||
`PlanningMapFactory.Create` 返回与来源输入隔离的不可变静态快照,不实现增量栅格更新或距离场局部修补。`PlanningGridMap` 保存各来源版本摘要、`InputFingerprint`、`OccupancyHash` 和通过 `Interlocked.Increment` 生成的进程内单调 `SnapshotId`。该计数器只标识快照,不保存地图内容。规划开始后只读取同一快照;上层即使收到新障碍物,也不得修改正在使用的占据缓冲区。
|
||||
|
||||
#### 两级指纹与快照复用
|
||||
|
||||
`PlanningMapCache` 是 `PlanningMapFactory` 的线程安全、容量为 4 的最近使用缓存;长期存在的 `CoarsePathPlanningService` 持有同一个工厂实例,因此多次规划可以复用快照。
|
||||
|
||||
每次创建按以下顺序判断:
|
||||
|
||||
1. 调用纯快照来源的 `ProjectToWorld`,按 `SourceId` 排序,并对边界、分辨率、空图策略、来源状态/版本和规范化世界几何体计算 `InputFingerprint`。
|
||||
2. 若缓存中存在相同 `InputFingerprint`,直接返回同一不可变 `PlanningGridMap`;不重新栅格化或生成距离场。
|
||||
3. 输入指纹不同时重新栅格化,并对最终连续占据 `byte[]` 计算 `OccupancyHash`。
|
||||
4. 若地图几何参数和 `OccupancyHash` 与缓存快照相同,复用占据缓冲区与距离场,只生成包含新来源摘要和新 `SnapshotId` 的轻量快照。
|
||||
5. `OccupancyHash` 不同时才重新执行距离场变换并缓存完整新快照。
|
||||
|
||||
浮点几何按其 IEEE 位模式和固定字段顺序计算确定性指纹,不通过简单的“坐标除以分辨率取整”判断变化,避免圆或矩形在格边附近发生漏失效。缓存项同时保留规范化输入描述;`InputFingerprint` 命中后仍执行结构相等比较。`OccupancyHash` 命中后仍比较地图几何参数和连续占据缓冲区长度/内容,不能只依赖哈希值判等。
|
||||
|
||||
起点、终点、车辆尺寸、安全余量、Hybrid A* 参数和可视化开关不属于地图指纹。TwoLeg 的检测状态从有效变为无检测/不可用/过期时,其来源结果必须改变;上层采集适配器负责根据检测有效期构造正确的 `TwoLegProjectionInput`,地图来源接口本身不读取系统时间。
|
||||
|
||||
#### 存储、坐标和查询优化
|
||||
|
||||
- 占据数据使用私有连续 `byte[]`,避免公开 `byte[,]` 带来的可变性和多维数组索引开销。
|
||||
- `IsOccupied(row,col)`、`IsOccupiedWorld(x,y)` 和保守距离查询保持无分配、常数复杂度;地图外直接返回占据或零净空。
|
||||
- 世界坐标到格索引使用 `floor((value - min) / resolution)`;`XMax`、`YMax` 排他。最后一个格子的几何上界必须裁剪到实际地图上界。
|
||||
- 构造阶段使用 `checked` 计算 `Rows * Cols`,继续采用 4,000,000 格绝对上限;任何溢出或超限在分配前返回失败结果。
|
||||
- 圆形与矩形栅格化只遍历其裁剪后的格索引包围盒,不扫描全图。与地图完全不相交的合法障碍物被忽略并记录在来源摘要中,而不是使地图构建失败。
|
||||
- `EnvironmentGridMap` 只有程序集内部的占据写入入口;`PlanningGridMap` 不提供任何写入入口,也不返回内部缓冲区引用。
|
||||
|
||||
#### 距离场优化
|
||||
|
||||
`EuclideanDistanceTransform` 使用两次一维平方距离变换完成精确二维栅格中心距离计算,时间复杂度为 `O(Rows × Cols)`,不得为每个自由格遍历全部障碍格。中间数组按行列最大长度复用,最终距离使用连续 `double[]` 保存。
|
||||
|
||||
`ObstacleDistanceField` 在精确中心距离上执行前述保守修正并封装查询,不允许调用方直接取得未经修正的中心距离用于碰撞放行。显式空图不运行无意义的变换,直接构造正无穷障碍距离场;地图边界仍由规划地图和车体碰撞检查独立约束。
|
||||
|
||||
#### 地图迁移顺序
|
||||
|
||||
1. 先建立 `MapBoundsMm`、新占据存储和纯栅格化测试,不修改旧 UI 入口。
|
||||
2. 建立 `PlanningMapFactory`、静态快照、距离场和规划查询测试。
|
||||
3. 将人工障碍、TwoLeg DTO 与现有 Ghost/固定场景迁移为统一 `IMapObstacleSource`。
|
||||
4. 建立两级指纹缓存与 `CoarsePathPlanningService` 一次调用入口。
|
||||
5. 将旧 `TrapMapImageExporter.cs` 拆分为 `Map/Test/Visualization` 下的五个文件,并将 PNG 导出与 `MovementTest.MapTest` 改为只消费新快照。
|
||||
6. 新旧地图回归结果一致后,退役旧地图构建、车体写入和图层合成入口,并更新或删除对应旧反射测试。
|
||||
|
||||
迁移期间不得让 Hybrid A* 同时支持新旧两种地图类型;规划器从第一天起只接受 `PlanningGridMap`。
|
||||
|
||||
## CoarsePath 模块
|
||||
|
||||
### 对外门面与契约
|
||||
### 一次调用编排门面
|
||||
|
||||
调用方只调用 `HybridAStarPlanner`:
|
||||
常规调用方只调用:
|
||||
|
||||
```csharp
|
||||
public sealed class CoarsePathPlanningService
|
||||
{
|
||||
public CoarsePathPlanningJobResult Plan(
|
||||
CoarsePathPlanningJob job,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
```
|
||||
|
||||
`CoarsePathPlanningJob` 集中以下输入:
|
||||
|
||||
```text
|
||||
PlanningMapRequest MapRequest
|
||||
Pose2D Start
|
||||
Pose2D Goal
|
||||
VehicleParameters Vehicle
|
||||
HybridAStarConfiguration Configuration
|
||||
double StartVehicleCurvature
|
||||
TravelDirection? StartDirection
|
||||
GoalDirectionConstraint GoalDirection
|
||||
PlanningDebugOptions Debug
|
||||
```
|
||||
|
||||
推荐调用形式:
|
||||
|
||||
```csharp
|
||||
var result = planningService.Plan(new CoarsePathPlanningJob
|
||||
{
|
||||
MapRequest = new PlanningMapRequest
|
||||
{
|
||||
Bounds = bounds,
|
||||
ResolutionMm = 50f,
|
||||
ObstacleSources = new IMapObstacleSource[]
|
||||
{
|
||||
new ManualObstacleSource(manualObstacles),
|
||||
new TwoLegObstacleSource(twoLegSnapshot),
|
||||
},
|
||||
AllowExplicitEmptyMap = true,
|
||||
},
|
||||
Start = startPose,
|
||||
Goal = goalPose,
|
||||
Vehicle = vehicle,
|
||||
Configuration = configuration,
|
||||
Debug = new PlanningDebugOptions
|
||||
{
|
||||
VisualizeMap = true,
|
||||
VisualizePath = true,
|
||||
},
|
||||
}, cancellationToken);
|
||||
```
|
||||
|
||||
一次调用内部固定执行:
|
||||
|
||||
```text
|
||||
PlanningMapFactory.Create(MapRequest)
|
||||
→ 地图失败则生成 FromMapFailure 结果
|
||||
→ HybridAStarPlanner.Plan(PlanningRequest, cancellationToken)
|
||||
→ IPlanningDebugSink 按 Debug 开关发布地图、路径和诊断
|
||||
```
|
||||
|
||||
`CoarsePathPlanningJobResult` 同时保留 `PlanningMapBuildResult MapResult` 与 `PlanningResult PlanningResult`,使调用方能够取得实际使用的 `PlanningGridMap` 快照、缓存命中情况和粗路径状态。地图失败时不启动搜索;调试发布失败只写入调试诊断,不改变地图或路径规划状态。
|
||||
|
||||
`PlanningDebugOptions` 只包含 `VisualizeMap`、`VisualizePath`、`VisualizeCollisionChecks` 等旁路开关。`IPlanningDebugSink` 由 Clumsy `MovementTest` 适配实现;核心服务默认使用空实现,因此无 UI 环境与自动化测试不加载 Painter。
|
||||
|
||||
### 纯搜索下层门面
|
||||
|
||||
需要复用已有地图快照或单独测试搜索时调用 `HybridAStarPlanner`:
|
||||
|
||||
```csharp
|
||||
public sealed class HybridAStarPlanner
|
||||
{
|
||||
public PlanningResult Plan(PlanningRequest request);
|
||||
public PlanningResult Plan(
|
||||
PlanningRequest request,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
```
|
||||
|
||||
`PlanningRequest` 组合 `PlanningGridMap`、起点 `Pose2D`、目标 `Pose2D`、`VehicleParameters` 与 `HybridAStarConfiguration`。它不接受地图构建器、UI 对象或传感器对象。
|
||||
`PlanningRequest` 组合以下不可变输入:
|
||||
|
||||
`HybridAStarConfiguration` 集中所有可调参数,包括最大节点数、超时、航向分辨率、原语长度、积分步长、曲率等级、代价权重、是否允许倒车、位置容差和航向容差。初始值遵循已有技术方案:0.50 m 原语、0.05 m 积分、5° 航向离散、五级曲率、0.15 m 位置容差、5° 航向容差。
|
||||
- `PlanningGridMap Map`
|
||||
- `Pose2D Start` 与 `Pose2D Goal`
|
||||
- `VehicleParameters Vehicle`
|
||||
- `HybridAStarConfiguration Configuration`
|
||||
- `double StartVehicleCurvature`,未提供时显式使用零曲率
|
||||
- `TravelDirection? StartDirection`,`null` 表示起步方向不受约束
|
||||
- `GoalDirectionConstraint GoalDirection`,取值为 `Any`、`Forward` 或 `Reverse`
|
||||
|
||||
`PlanningResult` 始终返回明确 `PlanningStatus`、诊断信息、零或一条 `IReadOnlyList<CoarsePathPoint>` 和 `IReadOnlyList<PathSegment>`。粗路径不包含时间、速度、加速度、舵轮角或轮速。
|
||||
它不接受地图构建器、UI 对象或传感器对象。起点曲率必须在车辆最大曲率内,并离散到最近的合法曲率等级;该索引作为起始搜索状态的一部分。
|
||||
|
||||
`VehicleParameters` 明确使用车辆几何中心为 `Pose2D` 参考点,并包含 `LengthMeters`、`WidthMeters`、`SafetyMarginMeters`、可选 `MaximumCurvaturePerMeter` 与可选 `MinimumTurningRadiusMeters`。最大曲率和最小转弯半径同时存在时使用更保守的限制;两者都未提供时请求无效。
|
||||
|
||||
`HybridAStarConfiguration` 集中所有可调参数,包括最大节点数、超时、航向分辨率、原语最大长度、积分步长、碰撞采样步长、曲率等级、代价权重、是否允许倒车、位置容差和航向容差。默认值为:
|
||||
|
||||
| 参数 | 默认值 |
|
||||
| --- | --- |
|
||||
| `PrimitiveLengthMeters` | 0.50 m,表示最大长度 |
|
||||
| `IntegrationStepMeters` | 0.05 m |
|
||||
| `MaximumCollisionCheckStepMeters` | 0.025 m,且运行时不得大于 `Map.ResolutionMeters / 2` |
|
||||
| `HeadingResolutionRadians` | 5° |
|
||||
| `CurvatureLevelCount` | 5 |
|
||||
| `GoalPositionToleranceMeters` | 0.15 m |
|
||||
| `GoalHeadingToleranceRadians` | 5° |
|
||||
| `MaximumExpandedNodes` | 200,000 |
|
||||
| `SearchTimeout` | 5 s |
|
||||
| `HeuristicWeight` | 1.0 |
|
||||
| `ReverseCostMultiplier` | 1.5 |
|
||||
| `GearSwitchPenaltyMeters` | 1.0 |
|
||||
| `CurvatureMagnitudeWeight` | 0.10 |
|
||||
| `CurvatureChangePenaltyMetersPerLevel` | 0.05 |
|
||||
| `ClearanceCostWeight` | 0.20 |
|
||||
| `ClearanceCostDistanceMeters` | 0.50 m |
|
||||
|
||||
搜索代价全部以“等效米”为单位:
|
||||
|
||||
```text
|
||||
primitiveCost =
|
||||
lengthMeters
|
||||
× directionMultiplier
|
||||
× (1
|
||||
+ CurvatureMagnitudeWeight × abs(curvature / maximumCurvature)
|
||||
+ ClearanceCostWeight × max(0, 1 - clearance / ClearanceCostDistanceMeters))
|
||||
+ gearSwitchPenalty
|
||||
+ CurvatureChangePenaltyMetersPerLevel × abs(curvatureLevelDelta)
|
||||
```
|
||||
|
||||
其中前进的 `directionMultiplier=1`,倒车使用 `ReverseCostMultiplier`;没有换向时 `gearSwitchPenalty=0`。所有权重必须为有限非负值。默认 `HeuristicWeight=1.0`;若调用方调大该值,只承诺更快地寻找可行解,不承诺离散图上的最低代价。
|
||||
|
||||
`PlanningResult` 始终返回明确 `PlanningStatus`、诊断信息、零或一条 `IReadOnlyList<CoarsePathPoint>` 和 `IReadOnlyList<PathSegment>`。成功结果中的点契约固定为:
|
||||
|
||||
```text
|
||||
CoarsePathPoint:
|
||||
X、Y m
|
||||
Heading、UnwrappedHeading rad
|
||||
ArcLength m,非负且不递减
|
||||
Direction Forward/Reverse
|
||||
VehicleCurvature 1/m
|
||||
BodyClearance m,保守下界
|
||||
IsGearSwitchPoint bool
|
||||
Source Start/MotionPrimitive/GoalTruncation
|
||||
```
|
||||
|
||||
`PathSegment` 固定包含 `SegmentIndex`、`Direction`、`StartIndex`、`EndIndex`、`StartsAtGearSwitch` 与 `EndsAtGearSwitch`。`StartIndex` 与 `EndIndex` 都是包含端点的索引,所有分段按索引顺序完整覆盖整条路径。换向时保留两个坐标和航向相同、弧长相同但方向不同的相邻点:前一点结束旧分段,后一点开始新分段并设置 `IsGearSwitchPoint=true`。除这种换向对外,装配器删除相邻重复点。粗路径不包含时间、速度、加速度、舵轮角或轮速。
|
||||
|
||||
### 车辆、碰撞和搜索
|
||||
|
||||
- `VehicleKinematics` 根据车辆参数提供最大曲率;直接最大曲率和最小转弯半径同时存在时采用更保守的值。
|
||||
- `HeadingFootprintTemplateCache` 为每一个离散航向预计算扩大车辆矩形覆盖的相对栅格偏移;扩大尺寸只在这里应用安全余量。
|
||||
- `FootprintCollisionChecker` 依次检查地图边界、距离场快速安全放行和精确矩形模板;它不执行搜索,也不改变地图。
|
||||
- `MotionPrimitiveGenerator` 仅生成恒曲率前进/倒车原语,并以不大于 0.05 m 的步长积分,保留内部积分点。
|
||||
- `VehicleFootprint` 以连续位姿计算扩大车辆矩形的四角、轴对齐包围盒和外接圆。安全余量只在这里同时加到长度和宽度两侧,不写入地图。
|
||||
- `OrientedRectangleCellIntersection` 使用分离轴定理判断连续位姿下的扩大车辆矩形是否与占据栅格矩形相交,不使用仅按离散航向和整数格偏移的模板,因此车辆中心的亚栅格偏移不会漏检。
|
||||
- `FootprintCollisionChecker` 依次执行扩大车体边界检查、保守距离场快速放行和包围盒内占据栅格的精确相交检查;它不执行搜索,也不改变地图。
|
||||
- `MotionPrimitiveGenerator` 仅生成恒曲率前进/倒车原语,以不大于 0.05 m 的步长保留输出积分点,并使用直线/圆弧解析公式更新位姿,不使用累计误差更大的显式欧拉积分。
|
||||
- 相邻碰撞检查位姿的中心位移不得超过 `min(MaximumCollisionCheckStepMeters, Map.ResolutionMeters / 2)`。同时用 `0.5 × (中心位移 + 外接圆半径 × 航向变化绝对值)` 作为扫掠附加余量检查相邻区间端点,保守覆盖两个采样位姿之间的车体运动;该附加余量只用于区间碰撞验证,不写入输出车体尺寸。
|
||||
- `GoalToleranceChecker` 只判断位置、航向和最后一段方向约束;位置和航向阈值来自 `HybridAStarConfiguration`。
|
||||
- `GridDijkstraHeuristic` 仅从目标在占据图上生成二维绕障距离启发;它不处理车辆运动学。
|
||||
- `HybridAStarSearch` 管理 Open List、Closed Set、节点扩展、代价、父索引与终点选择。Closed Set 键为位置格、航向格、方向和曲率等级。它不拼装最终路径。
|
||||
- `PathBacktracker` 从成功节点恢复原语的内部积分点;`CoarsePathAssembler` 去除相邻重复点、累计弧长、标记换向点并构造 `PathSegment`;`CoarsePathValidator` 用同一碰撞规则复核最终稠密输出。
|
||||
- 每条原语按积分点顺序执行数值合法性、碰撞和目标检查。如果某个内部积分点满足目标条件,当前原语在该点截断并生成终点候选;候选加入 Open List,只有当它作为最佳有效节点出队时才成功终止搜索。
|
||||
- `GridDijkstraHeuristic` 从目标在占据图上生成八邻域二维绕障距离启发,禁止穿过两个对角相邻障碍物的夹角;它不处理车辆运动学。若目标在二维图上不可达,规划返回 `NoFeasiblePath`。
|
||||
- `SearchCostCalculator` 只实现本节定义的等效米代价公式,集中处理倒车、换向、曲率、曲率变化与保守净空代价,不管理节点或 Open List。
|
||||
- `BinaryMinHeap` 是兼容 `netstandard2.0` 的内部最小堆,不依赖较新运行时的 `PriorityQueue`。排序依次使用 `F`、`H`、较大的 `G` 和单调递增插入序号,保证相同输入的搜索顺序可复现。
|
||||
- `HybridAStarSearch` 使用 `Dictionary<HybridAStarNodeKey,double>` 保存每个离散键当前最佳 `G`。发现更小 `G` 时允许重新打开节点;Open List 中的旧条目通过比较最佳 `G` 惰性丢弃。Closed Set 键为位置格、航向格、方向和曲率等级。
|
||||
- 搜索循环在扩展节点前检查取消、超时和节点上限。终点候选只有作为当前最佳有效节点出队时才返回成功。
|
||||
- `PathBacktracker` 只存父节点索引和原语描述,在成功后确定性地重新生成内部积分点,避免为所有搜索节点长期保存稠密点。`CoarsePathAssembler` 按既定换向规则累计弧长并构造 `PathSegment`。
|
||||
- `CoarsePathValidator` 使用相同的连续位姿、扫掠余量和碰撞规则复核最终稠密输出,同时检查有限数值、曲率上限、目标容差、方向约束、弧长单调性、换向对和分段索引完整覆盖。
|
||||
|
||||
第一版允许前进、倒车和换向。换向只可发生在原语边界;相邻原语曲率等级最多变化一级。目标达到容差即成功,不尝试 Reeds-Shepp 精确连接。
|
||||
|
||||
## 状态与失败处理
|
||||
|
||||
`PlanningStatus` 至少区分:`Success`、`InvalidRequest`、`InvalidMap`、`MapNotReady`、`InvalidVehicleParameters`、`InvalidCurvatureConfiguration`、`StartOutsideMap`、`StartInCollision`、`GoalOutsideMap`、`GoalInCollision`、`SearchTimeout`、`SearchNodeLimitExceeded`、`NoFeasiblePath`、`BacktrackingFailed`、`FinalValidationFailed` 与 `InternalError`。
|
||||
`PlanningStatus` 至少区分:`Success`、`Cancelled`、`InvalidRequest`、`InvalidMap`、`MapNotReady`、`InvalidVehicleParameters`、`InvalidCurvatureConfiguration`、`StartOutsideMap`、`StartInCollision`、`GoalOutsideMap`、`GoalInCollision`、`SearchTimeout`、`SearchNodeLimitExceeded`、`NoFeasiblePath`、`BacktrackingFailed`、`FinalValidationFailed` 与 `InternalError`。
|
||||
|
||||
所有输入错误在开始搜索前返回状态与可读原因;搜索或地图对象不能通过异常把部分路径发布给调用方。`PlanningDiagnostics` 记录扩展节点数、生成节点数、总路径长度、最小净空、耗时和终止原因,供之后的性能优化使用。
|
||||
所有输入错误在开始搜索前返回状态与可读原因;取消、超时和节点上限均返回空路径,不发布部分结果。除参数为空这类编程错误外,搜索或地图对象不能通过异常把部分路径发布给调用方。`PlanningDiagnostics` 记录扩展节点数、生成节点数、重新打开节点数、丢弃的陈旧堆条目数、Open List 峰值、总路径长度、最小保守净空、耗时和终止原因,供之后的性能优化使用。
|
||||
|
||||
## 测试设计
|
||||
|
||||
@@ -217,17 +503,42 @@ public sealed class HybridAStarPlanner
|
||||
| 测试文件/入口 | 覆盖内容 |
|
||||
| --- | --- |
|
||||
| `verify_planning_utils.ps1` | mm/m、deg/rad、角度环绕、车体/世界坐标变换和非法数值。 |
|
||||
| `verify_planning_map_adapter.ps1` | 圆与轴对齐矩形的中心放置和栅格化、人工与 TwoLeg 图层合并、AMR 自身不占据环境图、边界外保守占据、mm→m 深拷贝、距离场与 `PlanningReady`。 |
|
||||
| `verify_coarse_path_search.ps1` | 无障碍前进、单障碍绕行、允许倒车的狭窄场景、换向标记、位置/航向容差、越界/起终点碰撞/无解、曲率与 0.05 m 稠密点复核。 |
|
||||
| `verify_coarse_path_integration.ps1` | 由人工障碍地图构建、适配、规划、最终验证的端到端结果与诊断。 |
|
||||
| `verify_planning_map_factory.ps1` | 统一来源投影、必需/可选失败策略、来源确定性顺序、空图声明、输入指纹、占据哈希、完整/缓冲区缓存命中、来源版本摘要、静态快照隔离与并发访问。 |
|
||||
| `verify_planning_map_adapter.ps1` | 圆与轴对齐矩形的包围盒裁剪和格矩形相交、AMR 自身不占据环境图、连续行优先存储、坐标边界、非完整末格、越界保守占据、mm→m 深拷贝、距离场不高估与 `PlanningReady`。 |
|
||||
| `verify_planning_map_image.ps1` | 从 `PlanningGridMap` 渲染占据格、边界、起终点、车辆和路径叠加;覆盖关闭导出、非法尺寸、像素/文件上限、唯一命名、临时文件清理、PNG 结构与 CRC。 |
|
||||
| `verify_coarse_path_collision.ps1` | 车体中心位于栅格中心和亚栅格位置时的正交/45°/任意航向,边角擦碰、薄障碍、地图边界、距离场快速放行与原语区间扫掠碰撞。 |
|
||||
| `verify_coarse_path_search.ps1` | 无障碍前进、0.30 m 非整倍数终点截断、单障碍绕行、允许倒车的狭窄场景、起始曲率、目标进入方向、换向对、±π 航向容差、起点已满足目标、无解、取消、超时、节点上限、节点重新打开与确定性顺序。 |
|
||||
| `verify_coarse_path_integration.ps1` | `CoarsePathPlanningService` 一次调用完成多来源建图、缓存复用、规划、回溯和最终验证;复核调试开关不改变地图指纹或规划结果。 |
|
||||
| `benchmark_coarse_path.ps1` | Release 构建下的参考场景耗时、扩展节点数、Open List 峰值与托管内存增量。 |
|
||||
| `MovementTest.MapTest` | 在 Clumsy UI 中显示人工与 TwoLeg 投影后的环境栅格;可选 PNG 导出只用于调试证据。 |
|
||||
| `MovementTest.CoarsePathTest` | 使用固定可复现实例调用 `HybridAStarPlanner`,绘制地图、起终点、扩大车体检查点和粗路径;不向底盘发送运动命令。 |
|
||||
| `MovementTest.CoarsePathTest` | 使用固定可复现实例调用 `CoarsePathPlanningService`,绘制地图、起终点、扩大车体检查点和粗路径;`TestStop` 取消规划并清理 Painter,不向底盘发送运动命令。 |
|
||||
|
||||
每个新增公共契约均需有成功、边界和失败三类测试。测试场景由 `CoarsePathScenarioFactory` 统一生成,不在 `MovementTest` 中手写地图、原语或搜索细节。
|
||||
|
||||
`MovementTest.CoarsePathTest` 在后台任务中调用同步的 `CoarsePathPlanningService.Plan`,持有专用 `CancellationTokenSource`;`TestStop` 先取消规划,再清理任务引用和 Painter。UI 入口不得在界面线程上执行最长 5 s 的搜索,也不得调用任何底盘运动接口。
|
||||
|
||||
PowerShell 测试统一使用以下形式执行,绕过本机脚本执行策略差异,并在首个错误处停止:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\tests\<script>.ps1
|
||||
```
|
||||
|
||||
每个脚本首行设置 `$ErrorActionPreference = 'Stop'`。测试先执行一次项目构建,之后加载同一个 `bin/Debug/netstandard2.0/ClumsyPilot.dll`,不得混用 `obj` 与 `bin` 中的程序集。`netstandard2.0` 实现不得直接使用 `PriorityQueue`、`Math.Clamp`、`double.IsFinite` 或缺少兼容类型时的 `record/init`。
|
||||
|
||||
### 性能与资源验收
|
||||
|
||||
- 地图参考场景:20 m × 20 m、0.05 m 分辨率、160,000 格和 100 个圆/矩形障碍。Release 构建预热后连续构建 20 次,首次完整构建 P95 不超过 200 ms;完整快照缓存命中 P95 不超过 5 ms。
|
||||
- 地图极限场景:4,000,000 格、100 个障碍。完整构建必须在 3 s 内成功或以明确状态失败;成功时单次托管内存增量不超过 160 MB,不得出现整数溢出或部分发布快照。
|
||||
- 默认硬限制:`MaximumExpandedNodes=200000`、`SearchTimeout=5 s`;任一限制触发后必须在下一次循环检查点终止。
|
||||
- 参考场景:12 m × 8 m、0.05 m 分辨率、一个阻断直线路径的矩形障碍、起终点距离至少 8 m。Release 构建预热后连续运行 20 次,P95 规划耗时不超过 2 s,单次托管内存增量不超过 256 MB。
|
||||
- 压力场景:20 m × 20 m、0.05 m 分辨率、160,000 栅格。无论成功或无解,都必须在 5 s 与 200,000 扩展节点内返回,托管内存增量不超过 512 MB。
|
||||
- 性能脚本输出地图规模、状态、耗时、扩展/生成/重开节点数、Open List 峰值和内存增量;超过阈值返回非零退出码。
|
||||
|
||||
## 非目标与迁移边界
|
||||
|
||||
- 不修改 `TrajPlanner` 下的 Python 原型,也不把它作为运行时依赖。
|
||||
- 不在本阶段实现平滑、SQP、时间轨迹或控制接口;后续模块只消费 `PlanningResult` 中稳定的粗路径与方向分段。
|
||||
- 不保留将车辆自身写入规划占据图的兼容开关;若调试可视化需要车辆图形,应作为渲染叠加层。
|
||||
- 当前地图文件中的职责会按以上边界迁移;不会在迁移后继续向原 `Map_test` 大文件追加规划功能。
|
||||
- 现有 `Occupancygird_Map/Map_test` 原型中的通用栅格化、TwoLeg 投影和 PNG 调试能力按以上职责迁移。迁移完成后,旧 `GridMapData`、`TrapMapBuilder`、`TrapMapLayerComposer` 与旧 `TrapMapTest` 不再作为公共运行时入口;旧反射测试必须更新到新命名空间和门面,或在等价覆盖后删除。
|
||||
- PNG 导出器若保留,只能作为内部测试/可视化适配器消费只读 `PlanningGridMap`,不得重新拥有地图构建、障碍膨胀或车辆足迹写入逻辑。
|
||||
- 迁移验收必须证明 `CoarsePathPlanningService` 是推荐的一次调用入口,`PlanningMapFactory` 与 `HybridAStarPlanner` 只作为下层模块门面;旧地图构建器、投影器、栅格化器和搜索内部类型不得成为额外公共服务。
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# Hybrid A* P0 规划核心实施设计
|
||||
|
||||
## 目标
|
||||
|
||||
在已完成的 `PlanningGridMap` 静态地图能力之上,交付可复用、确定性且可验证的 Hybrid A* 粗路径规划核心。常规调用方通过 `CoarsePathPlanningService.Plan(job)` 一次完成建图和规划;纯算法测试可直接使用 `HybridAStarPlanner.Plan(request)`。
|
||||
|
||||
本设计只覆盖 P0-PLAN。P1 的 Clumsy UI 后台任务、Painter 绘制、Release 性能基准和旧 TrapMap 运行时入口退役不在本次实现范围内。
|
||||
|
||||
## 既有边界
|
||||
|
||||
- `Map` 模块只保存外部障碍物;不得写入 AMR 自身足迹,也不得对障碍物做车辆安全膨胀。
|
||||
- `PlanningGridMap` 是规划器唯一接受的地图类型,世界查询坐标为 m;越界位置视为占据且净空为 0。
|
||||
- 规划核心不读取传感器、定位、UI 或系统时间。取消令牌是唯一允许的外部控制输入。
|
||||
- 所有公共契约和核心逻辑兼容 `netstandard2.0`:不使用 `PriorityQueue`、`Math.Clamp`、`double.IsFinite`、`record` 或 `init`。
|
||||
- 注释延续已有模块风格:公开类型和成员写中文 XML 文档,说明单位、边界、返回/失败语义;内部复杂几何或搜索不变量保留简短中文行注释。
|
||||
|
||||
## 方案选择
|
||||
|
||||
采用“碰撞核心先行、搜索核心随后接入”的两段实现。
|
||||
|
||||
先建立公开数据契约、车辆扩大矩形和连续碰撞检查,使安全语义可以在不依赖搜索器的情况下通过自动化测试固定下来。随后在这些稳定边界上实现恒曲率运动原语、启发式、确定性 Open List、Hybrid A*、路径装配/复核和一次调用服务门面。此顺序避免 UI 或搜索状态掩盖车辆擦边、扫掠和地图边界错误。
|
||||
|
||||
## 架构与数据流
|
||||
|
||||
```text
|
||||
CoarsePathPlanningJob
|
||||
-> PlanningMapFactory.Create(MapRequest)
|
||||
-> PlanningMapBuildResult / PlanningGridMap
|
||||
-> HybridAStarPlanner.Plan(PlanningRequest)
|
||||
-> FootprintCollisionChecker
|
||||
-> MotionPrimitiveGenerator
|
||||
-> GridDijkstraHeuristic + HybridAStarSearch
|
||||
-> PathBacktracker + CoarsePathAssembler
|
||||
-> CoarsePathValidator
|
||||
-> CoarsePathPlanningJobResult
|
||||
```
|
||||
|
||||
`Facade` 只编排地图与规划,不接触栅格化、车辆几何或搜索节点。`HybridAStarPlanner` 在搜索前完成请求、地图、车辆、起点和终点的有效性检查;搜索成功后必须经过 `CoarsePathValidator` 才能发布路径。
|
||||
|
||||
### 公共契约
|
||||
|
||||
`Contracts` 定义 m/rad/1/m 单位的值对象、车辆参数、配置、请求、状态、诊断、路径点和分段。默认配置固定为:0.50 m 原语最大长度、0.05 m 积分步长、5° 航向分辨率、0.15 m 位置容差、5° 航向容差、200,000 节点和 5 s 搜索上限。
|
||||
|
||||
`PlanningResult` 对所有结果提供明确 `PlanningStatus` 和可读诊断。输入错误、地图未就绪、碰撞、无解、取消、超时、节点上限、回溯失败和最终校验失败都返回空路径;不会通过异常发布部分路径。
|
||||
|
||||
### 车辆碰撞
|
||||
|
||||
`VehicleFootprint` 将以车辆几何中心为参考的长宽与安全余量转换为扩大矩形、AABB 和外接圆。`VehicleKinematics` 从最大曲率和最小转弯半径推导更保守的最大曲率。
|
||||
|
||||
`FootprintCollisionChecker` 固定按以下顺序检查连续位姿:
|
||||
|
||||
1. 扩大矩形是否完整位于地图边界内;
|
||||
2. 使用保守距离场与外接圆进行严格大于关系的快速放行;
|
||||
3. 对包围盒内的每个占据格,使用 SAT 检查旋转矩形与格矩形是否相交或擦边;
|
||||
4. 对相邻采样位姿,以中心平移与航向变化构造扫掠附加余量,检查中间区间。
|
||||
|
||||
因此距离场只能加速安全放行,不能替代精确碰撞判定;安全余量只作用于车辆扩大矩形,绝不回写地图。
|
||||
|
||||
### 搜索与路径输出
|
||||
|
||||
第一版只生成前进、倒车和原语边界换向的恒曲率原语。运动积分使用直线/圆弧解析公式,积分点间距不超过配置步长;碰撞采样的中心位移不超过 `min(0.025 m, Map.ResolutionMeters / 2)`。
|
||||
|
||||
搜索键由位置格、航向格、方向和曲率等级组成。确定性二叉最小堆按 `F`、`H`、较大 `G` 和插入序号排序;更小 `G` 的状态允许重新打开,旧堆条目延迟丢弃。二维八邻域 Dijkstra 启发式禁止穿越障碍的对角夹角。
|
||||
|
||||
原语的每个内部积分点依次进行数值、碰撞和终点容差检查。首次满足终点条件时截断原语并将候选压入 Open List;只有该候选作为当前最优有效节点出队时才宣布成功。成功后由回溯器重建稠密积分点,由装配器生成累计弧长和包含式方向分段;换向处保留一对位置、航向和弧长相同但方向不同的相邻点。
|
||||
|
||||
### 文档
|
||||
|
||||
新增 `ClumsyPilot/ParkrobTrajplanner/CoarsePath/README.md`,作为 P0 调用者文档。它说明模块边界、一次调用示例、输入单位、来源版本与地图缓存关系、状态处理方式、路径输出含义和第一版非目标。现有 `Map/README.md` 不复制粗路径内容,继续保留地图构建细节。
|
||||
|
||||
## 错误与取消语义
|
||||
|
||||
- `PlanningGridMap` 为空、不可规划或起终点不在地图内时,在搜索前返回对应状态。
|
||||
- 起点/终点与扩大车辆矩形碰撞时,返回 `StartInCollision` 或 `GoalInCollision`。
|
||||
- 每次扩展节点前检查取消、超时和最大扩展数;触发后立即返回空路径和累计诊断。
|
||||
- 任意内部不变量异常被收敛为 `InternalError`,不向调用方泄露部分路径。
|
||||
- 地图构建失败时,服务直接包装 `PlanningMapBuildResult`,不启动 `HybridAStarPlanner`。
|
||||
|
||||
## 测试策略
|
||||
|
||||
测试继续使用真实 `netstandard2.0` 程序集的 PowerShell 反射脚本,所有 P0 生产代码均遵循 Red-Green-Refactor:先在脚本中写可反射调用的失败断言并确认其因类型或行为缺失失败,再写最小实现,最后运行同一脚本确认通过。
|
||||
|
||||
1. `verify_coarse_path_collision.ps1`:参数边界、亚栅格位姿、任意航向、擦边、薄障碍、地图边界、距离场放行和扫掠碰撞。
|
||||
2. `verify_coarse_path_search.ps1`:原语解析积分、非整倍数终点截断、方向约束、换向、启发式、堆确定性、重开、取消、超时、节点上限和无解。
|
||||
3. `verify_coarse_path_integration.ps1`:从多来源 `PlanningMapRequest` 到最终路径的服务编排、地图缓存复用、最终复核和调试开关不改变规划结果。
|
||||
|
||||
每个脚本在测试前构建 `ClumsyPilot/ClumsyPilot.csproj`,随后只加载 `bin/Debug/netstandard2.0/ClumsyPilot.dll`。完成 P0 前必须重新运行构建与所有既有 Map 验证脚本,确保新规划代码没有破坏地图模块。
|
||||
|
||||
## P0 验收
|
||||
|
||||
- 固定静态场景可以经 `CoarsePathPlanningService.Plan` 得到连续、无碰撞、满足终点容差的稠密粗路径。
|
||||
- 最终路径通过同一套扩大车体和扫掠规则复核;失败不发布部分路径。
|
||||
- 路径点、方向分段、状态和诊断可由不依赖 Clumsy 的调用方直接消费。
|
||||
- 使用说明可独立解释 Map 与 CoarsePath 边界、单位、调用方式及版本限制。
|
||||
@@ -0,0 +1,56 @@
|
||||
# Map 模块文档与注释设计
|
||||
|
||||
## 目标
|
||||
|
||||
让阅读 `ClumsyPilot/ParkrobTrajplanner/Map` 的开发者无需反查实现,即可理解模块文件职责、建图数据流和所有公共 API 的调用契约。
|
||||
|
||||
## 交付内容
|
||||
|
||||
### `Map/README.md`
|
||||
|
||||
README 是 Map 模块的入口说明,只保留不会由 IDE 自动展示的模块级信息:
|
||||
|
||||
- 当前目录树,以及每个文件的一句话职责;
|
||||
- 从 `PlanningMapRequest` 到 `PlanningGridMap` 的建图数据流;
|
||||
- 坐标系和单位约定;
|
||||
- `PlanningMapFactory` 的最小调用示例;
|
||||
- 缓存与 `SourceVersion` 的使用约束;
|
||||
- 测试、PNG 调试和旧 TrapMap 的边界。
|
||||
|
||||
README 不复制逐个参数说明;参数的唯一权威说明位于声明处的代码注释。
|
||||
|
||||
### `.cs` 代码注释
|
||||
|
||||
覆盖 `Map` 内所有 `public` 类、接口、枚举、构造函数、方法和属性。注释使用可被 C# IDE 识别的 `///` XML 文档注释,但按 Python docstring 的阅读顺序组织:
|
||||
|
||||
1. 功能:该成员做什么;
|
||||
2. 参数:名称、类型语义、单位、可空性或约束;
|
||||
3. 返回:返回对象及字段的业务意义;
|
||||
4. 注意:缓存、坐标转换、不可变性、线程安全或失败语义等调用者必须知道的约束。
|
||||
|
||||
不为纯私有实现逐项添加重复注释;复杂算法的私有方法只在其现有说明明显不足、且会妨碍维护时补充最小必要说明。
|
||||
|
||||
## 注释示例
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 创建规划地图快照。
|
||||
///
|
||||
/// 参数:
|
||||
/// - request:建图请求,包含世界范围、栅格分辨率和障碍物来源。
|
||||
///
|
||||
/// 返回:
|
||||
/// - PlanningMapBuildResult:成功时含不可变地图、来源投影结果和缓存命中类型。
|
||||
///
|
||||
/// 注意:
|
||||
/// - 应长期复用工厂实例,才能复用缓存。
|
||||
/// </summary>
|
||||
public PlanningMapBuildResult Create(PlanningMapRequest request)
|
||||
```
|
||||
|
||||
## 验收
|
||||
|
||||
- `Map/README.md` 可独立说明文件结构、数据流和公共入口;
|
||||
- 通过 `rg` 检查,所有 Map 公共 API 均有紧邻的中文 `///` 文档说明;
|
||||
- 现有 Map Gate 与项目编译保持通过;
|
||||
- 不修改 Map 的建图算法、缓存键或运行时行为。
|
||||
@@ -0,0 +1,114 @@
|
||||
# P1 粗路径 Clumsy UI 集成设计
|
||||
|
||||
## 目标
|
||||
|
||||
在不改变 P0 地图、Hybrid A* 与碰撞安全语义的前提下,为 Clumsy 增加可手动运行的粗路径场景测试。使用者能够从 MovementTest 列表启动固定场景,或传入 AMR 当前世界位姿并手动输入终点;界面显示栅格地图、起点、终点、连续路径、换向点和扩大车辆矩形检查点,并可随时停止正在进行的规划。
|
||||
|
||||
本设计只覆盖 P1 的首个交付:场景工厂、后台 MovementTest、Painter 可视化、自动化集成检查和模块 README。Release 性能基准属于 P1 的下一项交付;旧 TrapMap 迁移、旧验证脚本和旧入口的清理按已确认范围排除。
|
||||
|
||||
## 既有边界
|
||||
|
||||
- 业务入口仍唯一为 `CoarsePathPlanningService.Plan(job, cancellationToken)`;MovementTest 不自行拼接 `PlanningMapFactory`、`HybridAStarPlanner`、栅格化器、碰撞器、原语或搜索节点。
|
||||
- `PlanningMapRequest` 的边界、分辨率和障碍物几何使用 mm;`CoarsePathPlanningJob` 位姿、车辆尺寸和路径点使用 m,航向使用 rad。
|
||||
- 项目传入的 AMR 位姿采用世界 `X/Y(mm)` 与航向 `th(deg)`。P1 只在 UI 边界将其一次性转换为 `Pose2D(X / 1000, Y / 1000, th × pi / 180)`;规划核心不接受度或 mm 位姿。当前车队路径代码将来自 `getCartLocation().th` 的姿态与度制角相加,并在调用三角函数前显式除以 180 再乘 pi,因此 P1 不沿用旧 `Movements.cs` 直接对 `.th` 调用 `Math.Cos/Sin` 的不一致写法。
|
||||
- AMR 起点必须表示车辆几何中心。若上游定位的参考点是雷达、天线或其他安装点,上游必须先按外参转换到车辆几何中心;安全余量仍只由 `VehicleParameters` 表达。
|
||||
- `PlanningResult` 只有 `Success` 才能携带完整路径;取消、超时、无解和失败不得在 UI 上表现为部分路径。
|
||||
- `Painter` 在现有后台多车线程中已被调用,因此本设计允许规划任务完成后的后台回调操作该图层;规划核心本身始终不依赖 UI。
|
||||
- 注释延续 P0 风格:公开类型与成员使用中文 XML 文档,说明单位、并发/停止语义和返回行为;会影响竞态的内部代码保留简短中文行注释。
|
||||
|
||||
## 方案选择
|
||||
|
||||
采用“六个薄 MovementTest 入口 + 共享后台执行器”的方案。
|
||||
|
||||
每个入口对应一个已命名的固定场景,便于在 Clumsy 的测试列表中直接运行;它们共用同一个静态 `CoarsePathPlanningService`,因此既能复用地图缓存,也不会让测试代码绕开门面。一个位于同一源文件内的执行器负责互斥会话、`Task` 生命周期、取消和绘制,避免六个入口复制并发逻辑。
|
||||
|
||||
不采用单一测试入口配合代码常量切换,因为手动验证需要反复改代码;也不采用运行时弹窗选项,因为这会增加 UI 输入状态和无法直接观察每个场景的可发现性。
|
||||
|
||||
## 文件与职责
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| `ClumsyPilot/ParkrobTrajplanner/CoarsePath/Test/CoarsePathScenarioFactory.cs` | 创建不读取 UI、传感器、定位或时钟的固定 `CoarsePathPlanningJob` 场景。每次创建均返回新请求对象。 |
|
||||
| `ClumsyPilot/ParkrobTrajplanner/CoarsePath/Test/MovementTest.CoarsePathTest.cs` | 声明七个 MovementTest 入口,以及共享服务、后台执行、取消、结果日志、AMR 位姿/手动终点输入与 Painter 绘制。 |
|
||||
| `ClumsyPilot/tests/verify_coarse_path_integration.ps1` | 通过真实程序集反射验证场景、门面调用约束、缓存、换向、无解、取消和测试代码结构。 |
|
||||
| `ClumsyPilot/ParkrobTrajplanner/CoarsePath/README.md` | 补充 P1 手动测试方法、颜色图例、单位转换、停止语义和非目标。 |
|
||||
|
||||
所有 UI 辅助类型保留在 `CoarsePath/Test` 内;不会向 `Map`、`Search`、`Vehicle` 或 `Facade` 增加 UI 依赖。
|
||||
|
||||
## 场景工厂
|
||||
|
||||
`CoarsePathScenarioFactory` 公开一个场景枚举和按枚举创建请求的方法。工厂的职责仅是构造纯输入;它不持有服务、缓存、Painter 或取消源。每个请求采用同一组可验证的车辆和搜索默认值,再按场景覆盖障碍物、起终点和方向约束。
|
||||
|
||||
场景固定使用世界 mm 地图边界和分辨率,向 `Pose2D` 写入对应的 m 坐标。障碍物只通过 `ManualObstacleSource` 和 `TwoLegObstacleSource` 进入 `PlanningMapRequest`,并为内容变化提供固定且正确的 `SourceVersion`。
|
||||
|
||||
| 场景 | 地图与预期 |
|
||||
| --- | --- |
|
||||
| 显式空图 | `AllowExplicitEmptyMap=true`,直达前进路径成功,用于检查最短调用链。 |
|
||||
| 单矩形绕行 | 中央矩形阻断直线,路径成功且必须绕障。 |
|
||||
| 手工圆、矩形与 TwoLeg | 同时使用手工圆形、手工矩形和有效 TwoLeg 快照,路径成功,证明多来源经过同一门面。 |
|
||||
| 缓存命中 | 连续以新建但完全相同的输入调用同一服务两次;第二次 `MapResult.CacheHit` 必须为 `Input`。 |
|
||||
| 倒车换向 | 起步方向限制为前进、终点进入方向限制为倒车;成功路径必须出现标记的换向点。 |
|
||||
| 无解 | 完全贯穿地图的障碍带隔开起点和终点,返回 `NoFeasiblePath` 且无路径。 |
|
||||
| AMR 位姿与手动终点 | 起点使用上层传入并冻结的 AMR 世界位姿;操作者输入同一世界系的终点 X/Y/航向。该入口仅使用明确提供的障碍物快照,显式空图只能作为演示,不能代表现场无障碍。 |
|
||||
|
||||
实现期间先用自动化断言固定每个场景的状态;若需为当前 P0 运动原语调整数值,只能调整场景几何或请求参数,不能放宽碰撞、目标或失败语义。
|
||||
|
||||
## 后台会话与取消
|
||||
|
||||
共享执行器持有一个静态、长期存活的 `CoarsePathPlanningService`。任一入口启动时会创建新的会话:运行编号、专用 `CancellationTokenSource`、场景描述和后台 `Task`。启动新会话前取消旧会话,以确保同时最多只有一个可绘制的规划结果。
|
||||
|
||||
AMR 位姿和手动终点在创建任务前被转换、有限值校验并冻结,随后只作为 `CoarsePathPlanningJob` 数据传给后台。MovementTest 不在规划后台持续读取定位;若未来接入可能阻塞的 `DetourInterface.getCartLocation()`,它必须位于独立的上游快照提供者,不能阻塞 UI 或绕过本设计的输入契约。
|
||||
|
||||
```text
|
||||
MovementTest.Test
|
||||
-> 生成场景的全新 CoarsePathPlanningJob
|
||||
-> 创建运行编号和 CancellationTokenSource
|
||||
-> Task.Run(() => service.Plan(job, token))
|
||||
-> 完成回调:仅当运行编号仍为当前会话时记录并绘制结果
|
||||
|
||||
MovementTest.TestStop
|
||||
-> 取消当前 CancellationTokenSource
|
||||
-> 使当前运行编号失效并解绑 Task 引用
|
||||
-> 清空专用 Painter 图层
|
||||
-> 旧任务完成后只释放其 CancellationTokenSource,不再绘制
|
||||
```
|
||||
|
||||
`Test` 绝不等待 `Task`、不读取 `Task.Result`,因此不会阻塞 Clumsy 界面。`TestStop` 不等待规划任务退出;P0 的共享预算会将令牌传递至建图、EDT、Dijkstra 和 Hybrid A*,任务在其检查点返回 `Cancelled`。运行编号检查可防止已取消的旧任务在新任务结果之后覆盖画面。
|
||||
|
||||
任务异常只记录清晰的测试诊断并释放资源,不伪造 `PlanningResult`。正常停止、超时、无解与输入失败均使用门面实际返回的状态。
|
||||
|
||||
所有七个入口只创建规划请求、任务和绘制;不得引用 `BasicPilotBase.Chassis`、`SendMotion`、`DriveTask` 或任何底盘控制 API。
|
||||
|
||||
## 绘制规则
|
||||
|
||||
使用独立的全局世界坐标 Painter 图层,例如 `CoarsePathPlanningV1`。Painter 输入为 mm,因此所有来自 `Pose2D` 与 `CoarsePathPoint` 的 X/Y 必须乘以 1000;航向仍以 rad 计算旋转矩形。不得混用 Map 的 mm 和 CoarsePath 的 m。所有地图输入、AMR 起点和手动终点均处于同一个世界坐标系。
|
||||
|
||||
- 先绘制地图 `[XMin, XMax) × [YMin, YMax)` 的粗外边界、世界 X/Y 参考和栅格网络。格线遵循真实 `ResolutionMm`;当格线数量超过显示上限时,按整数格距抽稀,并在状态文本中保留真实分辨率与显示步距。
|
||||
- 从 `PlanningGridMap.IsOccupied(row, col)` 绘制占据格,而不是重新绘制原始障碍物几何;因此显示内容与实际规划快照一致。空闲格使用背景,不为每个空格增加填充。
|
||||
- 起点为绿色圆、方向短线和“起点”标签;终点为橙色圆、方向短线、“终点”标签及目标位置容差圈。
|
||||
- 成功路径逐段连接:前进与倒车使用不同颜色,并以固定间距绘制方向箭头;路径不成功时不绘制任何路径段。
|
||||
- `IsGearSwitchPoint=true` 的点使用紫色标记和“换向”标签。
|
||||
- 对首点、末点、每个换向点和固定间隔点绘制旋转矩形。矩形半长/半宽为 `Vehicle.LengthMeters / 2 + SafetyMarginMeters` 和 `Vehicle.WidthMeters / 2 + SafetyMarginMeters`,仅用于显示 P0 已采用的扩大车体,不参与碰撞判断。
|
||||
- 在地图角落绘制固定图例:边界、占据格、起点、终点、前进、倒车、换向与扩大车体检查框的颜色含义。无论成功与否,绘制文本状态:场景名称、地图快照 ID、地图构建状态、缓存层级、规划状态、耗时和终止原因。停止或新会话开始时先清空旧图层。
|
||||
|
||||
绘制只消费 `CoarsePathPlanningJobResult` 的只读结果;不修改 `PlanningMapRequest`、地图快照、路径、调试开关或服务缓存。
|
||||
|
||||
## 测试与验收
|
||||
|
||||
按 Red-Green-Refactor 顺序扩展 `verify_coarse_path_integration.ps1`:先增加以下会失败的反射/行为断言,再实现最小代码,最后运行相同脚本。
|
||||
|
||||
1. 断言 `CoarsePath/Test` 中的场景工厂和 MovementTest 文件存在;工厂提供六类固定场景与一个 AMR 位姿/手动终点入口,且每次创建返回独立请求。
|
||||
2. 使用同一 `CoarsePathPlanningService` 运行工厂场景:空图、矩形、多来源和倒车换向均成功;倒车换向路径含 `IsGearSwitchPoint`;无解结果为 `NoFeasiblePath` 且路径为空。
|
||||
3. 对缓存场景连续调用两次,断言第二个地图结果为 `Input` 命中,且路径状态和点数不因缓存改变。
|
||||
4. 断言 AMR `0 deg`、`90 deg` 的 UI 输入分别转换为 `0 rad`、`pi/2 rad`,同时 X/Y 由 mm 转为 m;手动目标与起点都使用相同转换与有限值校验。
|
||||
5. 对预先取消的后台调用断言门面映射为 `Cancelled`、地图或路径不发布部分结果;结构检查确认 MovementTest 使用 `Task.Run` 和 `CancellationTokenSource`,且没有等待任务。
|
||||
6. 结构检查确认测试入口只通过 `CoarsePathPlanningService` 进行规划,且不引用底盘命令、栅格化器、碰撞器、原语生成器或搜索节点;并检查绘制代码消费 `PlanningGridMap` 的边界、分辨率与占据状态,包含图例和成功路径保护。
|
||||
7. 更新 README 断言,确认 P1 的 UI、AMR 位姿单位、手动终点、取消和“无底盘命令”边界可被调用方查阅。
|
||||
|
||||
完成后运行 Debug 构建及现有 P0 Map/CoarsePath 验证脚本(不恢复或改动已被排除的旧 TrapMap 验证脚本)。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不实现路径平滑、速度规划、跟踪控制、底盘命令、实时重规划或传感器采集。
|
||||
- 不改变地图指纹、缓存键、障碍物栅格化、车辆碰撞、终点判定、搜索代价或资源上限。
|
||||
- 不在本交付中实现 Release 性能基准,也不清理、迁移或恢复任何 TrapMap 文件与脚本。
|
||||
@@ -0,0 +1,59 @@
|
||||
# 规划操作预算与诊断收尾设计
|
||||
|
||||
## 目标
|
||||
|
||||
在进入 P1 的 UI 集成前,使一次 `CoarsePathPlanningService.Plan` 调用的取消与超时语义覆盖完整链路:地图创建、距离场构建、二维 Dijkstra 启发式和 Hybrid A* 搜索。同时让现有 `PlanningDiagnostics` 中的 Open List 陈旧条目数与峰值容量反映真实搜索数据。
|
||||
|
||||
成功、无解、输入无效和碰撞安全语义不改变;任何取消或超时结果均不得发布部分路径或部分地图。
|
||||
|
||||
## 方案选择
|
||||
|
||||
采用一个内部共享的、基于单调 `Stopwatch` 的 `PlanningOperationBudget`。它保存调用方的 `CancellationToken`、整次调用开始时刻与总超时,并在每个耗时循环中返回三态结果:继续、已取消、已超时。
|
||||
|
||||
不采用“只在 Dijkstra 前后检查”的方案,因为大图 Dijkstra 和 EDT 仍可能长时间无响应;也不替换 Dijkstra 启发式,避免在收尾阶段改变 Hybrid A* 的搜索特性。
|
||||
|
||||
## 边界与数据流
|
||||
|
||||
```text
|
||||
CoarsePathPlanningService.Plan(job, token)
|
||||
-> PlanningOperationBudget(token, job.Configuration.SearchTimeout)
|
||||
-> PlanningMapFactory.Create(mapRequest, budget)
|
||||
-> EnvironmentMapBuilder / PlanningMapAdapter / EDT
|
||||
-> HybridAStarPlanner.Plan(planningRequest, budget)
|
||||
-> GridDijkstraHeuristic
|
||||
-> HybridAStarSearch Open List
|
||||
-> PlanningResult + PlanningDiagnostics
|
||||
```
|
||||
|
||||
`PlanningOperationBudget` 放在不依赖 `Map` 或 `CoarsePath` 的公共工具层,仅暴露中立的停止原因。Map 与粗规划分别把该原因映射到自己的结果类型,避免 `Map` 反向依赖 `CoarsePath`。
|
||||
|
||||
地图构建结果增加明确的终止状态(成功、普通构建失败、取消、超时)。`CoarsePathPlanningService` 将地图阶段的取消映射为 `PlanningStatus.Cancelled`,地图阶段的超时映射为 `PlanningStatus.SearchTimeout`;两种结果都保留地图构建诊断但路径和分段为空。
|
||||
|
||||
现有不带预算参数的 `PlanningMapFactory.Create`、`HybridAStarPlanner.Plan` 和 `GridDijkstraHeuristic` 入口保持可用,作为不受取消限制的兼容包装;业务门面只使用带共享预算的内部入口。
|
||||
|
||||
## 响应与一致性规则
|
||||
|
||||
- 每个耗时循环在开始处及每处理最多 256 个工作单元后检查预算;检查不改变正常情况下的栅格、启发式或 Open List 排序。
|
||||
- 等待地图工厂创建锁时使用可轮询的获取方式,以便取消和超时也能中断排队等待。
|
||||
- 缓存命中仍立即返回原有不可变快照;预算已停止时优先返回取消/超时,不能借缓存绕过调用方停止请求。
|
||||
- 已被取消或超时的地图构建不得写入任何缓存,也不得发布部分 `PlanningGridMap`。
|
||||
- 规划器总耗时从门面开始计时;搜索器不重新开始独立的 5 秒窗口。
|
||||
|
||||
## 诊断
|
||||
|
||||
`HybridAStarSearchResult` 增加陈旧 Open List 条目数和 Open List 峰值。每次丢弃失效的普通节点时递增陈旧计数;每次成功入堆后更新峰值。`HybridAStarPlanner` 原样将这两个统计写入 `PlanningDiagnostics`。
|
||||
|
||||
目标候选仍保留其当前规则:不受普通离散键的 best-G 压制,出队时复核。它们占用 Open List 容量,因此计入峰值;不因候选自身而计为陈旧条目。
|
||||
|
||||
## 测试与验收
|
||||
|
||||
- 在大于一个检查批次的地图上,Dijkstra 预计算期间取消,断言返回 `Cancelled`、空路径和有限响应时间。
|
||||
- 使用足以覆盖 Dijkstra 工作的极短总超时,断言返回 `SearchTimeout`、空路径,且总耗时不超出预算一个检查批次的合理余量。
|
||||
- 在地图适配器/EDT 处理中取消和超时,断言地图结果带对应状态、没有地图快照且缓存未被污染。
|
||||
- 使用产生失效 Open List 条目的场景,断言陈旧数大于零;任意正常搜索断言峰值至少为一,并与最终 `PlanningDiagnostics` 一致。
|
||||
- 重新运行 Debug 构建、所有既有 P0 地图/粗规划检查,以及新增取消、超时和诊断检查。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不修改运动原语、碰撞保守性、代价公式、目标候选排序或路径装配。
|
||||
- 不在本次收尾中实现 UI、Painter、场景工厂或 Release 性能门槛;这些仍属于 P1。
|
||||
@@ -60,9 +60,9 @@ Create(CoarsePathTestScenario scenario,
|
||||
|
||||
## 缓存语义
|
||||
|
||||
缓存键继续由完整的地图输入决定。由于地图边界和障碍随 AMR 坐标平移,只有两次固定案例使用完全相同的 AMR 位姿时,`粗路径规划-缓存命中` 才会命中现有输入缓存。
|
||||
缓存键继续由完整的地图输入决定。由于地图边界和障碍随 AMR 坐标平移,两次固定案例冻结到相同的 AMR X/Y、从而形成相同的平移后地图输入时,`粗路径规划-缓存命中` 才会命中现有输入缓存。仅 AMR 航向变化不会改变地图输入,缓存仍可命中。
|
||||
|
||||
AMR 已移动时显示缓存未命中是正确结果,而不是规划失败。UI 继续显示实际的缓存状态。
|
||||
AMR 位置已移动时显示缓存未命中是正确结果,而不是规划失败。UI 继续显示实际的缓存状态。
|
||||
|
||||
## 验证
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# 粗路径搜索耗时设计
|
||||
|
||||
## 目标
|
||||
|
||||
为粗路径规划结果增加独立的“路径搜索耗时”。它用于回答:在规划地图已经可用后,从起点到终点得到可发布最终粗路径实际花费了多久。
|
||||
|
||||
现有 `PlanningDiagnostics.Elapsed` 保持不变,继续表示从 `CoarsePathPlanningService.Plan` 入口开始的总耗时。
|
||||
|
||||
## 计时边界
|
||||
|
||||
`PlanningDiagnostics.PathSearchElapsed` 的边界固定如下:
|
||||
|
||||
- 开始:`HybridAStarPlanner` 已完成输入、起终点和初始碰撞检查,即将调用 `HybridAStarSearch.Search`。
|
||||
- 包含:二维 Dijkstra 启发式预计算、Hybrid A* 节点扩展、路径回溯、路径装配、方向分段和最终碰撞复核。
|
||||
- 结束:规划器准备返回对应的 `PlanningResult`。
|
||||
- 不包含:地图来源读取、地图缓存查询、障碍物栅格化、距离场构建,以及门面层在进入规划器前的工作。
|
||||
|
||||
因此,此字段表示“地图就绪后的路径求解与发布耗时”,而不是仅 Open List 循环的耗时。
|
||||
|
||||
## 数据契约
|
||||
|
||||
在 `PlanningDiagnostics` 新增只读 `TimeSpan PathSearchElapsed`:
|
||||
|
||||
- 成功时记录完整路径搜索与发布阶段耗时。
|
||||
- 搜索失败、无解、节点上限、超时、取消、回溯失败、装配失败或最终复核失败时,记录截至返回前已消耗的该阶段时间。
|
||||
- 在进入搜索阶段前即失败(例如输入、起终点或初始碰撞检查失败)时为 `TimeSpan.Zero`。
|
||||
- 该字段必须为非负值,并且不超过总耗时 `Elapsed`。
|
||||
|
||||
保持构造函数的现有调用兼容:新参数具有 `TimeSpan.Zero` 默认值。`HybridAStarPlanner` 是唯一写入实际计时值的边界。
|
||||
|
||||
## 实现方案
|
||||
|
||||
推荐方案是在 `HybridAStarPlanner.Plan` 中,于调用 `_search.Search` 前创建本地 `Stopwatch`,并在所有搜索后返回路径将要构造 `PlanningResult` 时读取其 `Elapsed`。`CreateDiagnostics` 接收这个独立耗时,并写入 `PlanningDiagnostics`。
|
||||
|
||||
选择该方案的原因:
|
||||
|
||||
- 不修改门面的共享总预算和取消/超时语义。
|
||||
- 不让 `HybridAStarSearch` 暴露计时实现细节。
|
||||
- 计时覆盖用户定义的完整粗路径产出阶段,而非只覆盖节点扩展循环。
|
||||
|
||||
未采用的方案:
|
||||
|
||||
1. 直接复用 `PlanningOperationBudget.Elapsed`:会包含建图,不满足需求。
|
||||
2. 仅在 `HybridAStarSearch` 内计时:会遗漏回溯、装配和最终复核,无法表示最终粗路径产出时间。
|
||||
3. 为建图、启发式、搜索、复核分别公开多组指标:诊断更细,但超出当前需求。
|
||||
|
||||
## 可视化与文档
|
||||
|
||||
`MovementTest.CoarsePathTest` 的状态图层和 Toast 同时显示:
|
||||
|
||||
```text
|
||||
总耗时:<Elapsed> ms,路径搜索:<PathSearchElapsed> ms
|
||||
```
|
||||
|
||||
README 明确区分:总耗时覆盖建图和路径规划;路径搜索耗时仅覆盖地图就绪后的最终粗路径搜索、回溯、装配与复核。
|
||||
|
||||
## 验证
|
||||
|
||||
自动化验证应覆盖:
|
||||
|
||||
1. `PlanningDiagnostics` 默认搜索耗时为零,且新字段可由调用方读取。
|
||||
2. 一个真实可行规划返回非负的路径搜索耗时,且不大于总耗时。
|
||||
3. 既有总预算、取消、超时和路径状态断言不改变。
|
||||
4. UI 源码检查确认图层和 Toast 读取并显示新字段。
|
||||
5. README 包含新字段的计时边界说明。
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# CoarsePath README 结构化重构设计
|
||||
|
||||
## 目标
|
||||
|
||||
将 `ClumsyPilot/ParkrobTrajplanner/CoarsePath/README.md` 重构为与 `Map/README.md` 一致的说明风格,使调用者能够从模块职责、文件位置和数据流开始,逐步理解粗路径的调用、状态处理、P1 手动测试与明确的非目标。
|
||||
|
||||
本次只重构文档内容与现有文档检查;不改变 `Map`、`CoarsePath`、P1 UI 或任何测试场景的运行行为。
|
||||
|
||||
## 当前事实
|
||||
|
||||
- `Map` 负责障碍物来源、栅格化、不可变 `PlanningGridMap` 与缓存;它是粗路径的输入依赖。
|
||||
- `CoarsePath` 已具备 P0 核心:车辆扩大足迹碰撞、前进/倒车原语、Dijkstra 启发式、Hybrid A*、路径回溯、最终复核与业务门面。
|
||||
- P1 已具备:六个固定场景、AMR 位姿与手动终点空图演示、后台取消、Painter 结果可视化与 UI 结构检查。
|
||||
- 尚不包含平滑、速度/时间轨迹、底盘控制、实时重规划、真实作业障碍物接入与 Release 基准。
|
||||
|
||||
## README 目标结构
|
||||
|
||||
1. **模块说明**:定义 `CoarsePath` 的输入、输出、唯一业务入口与职责边界。
|
||||
2. **文件结构**:按 `Contracts`、`Vehicle`、`Search`、`Output`、`Facade`、`Test` 列出实际文件及职责。
|
||||
3. **规划数据流**:说明 `CoarsePathPlanningJob` 经服务、Map 快照、Hybrid A* 到 `PlanningResult` 的固定路径;明确地图失败不会启动搜索。
|
||||
4. **状态、单位与安全边界**:集中说明 mm/m、deg/rad、车辆安全外扩、取消/超时和“非成功不发布部分路径”。
|
||||
5. **最小调用示例**:沿用现有可编译门面调用,展示成功、地图失败和规划失败的处理方式。
|
||||
6. **缓存与 SourceVersion**:解释长期持有服务、`Input`/`Occupancy`/`None` 缓存层级及版本递增责任。
|
||||
7. **详细使用指南**:依次说明长期服务、准备地图请求、车辆和搜索参数、调用门面、消费路径与方向段。
|
||||
8. **P1 测试与调试**:集中说明七个 MovementTest、AMR 手动终点单位边界、后台停止和 Painter 图例。
|
||||
9. **常见错误**:用“现象 / 原因 / 处理”表格覆盖单位混用、遗漏 `SourceVersion`、隐式空图、错误处理失败结果、将粗路径当作控制轨迹等问题。
|
||||
10. **第一版限制**:保留不属于 P0/P1 的能力清单。
|
||||
|
||||
## 内容约束
|
||||
|
||||
- 仅记录已实现且已验证的行为;不把 P1 计划或人工验收说成已完成能力。
|
||||
- 固定使用 `CoarsePathPlanningService.Plan(job, cancellationToken)` 作为唯一业务调用示例;不鼓励 UI 直接组装搜索组件。
|
||||
- 保留 `Map/README.md` 链接,避免复制地图障碍物和栅格化的详细说明。
|
||||
- P1 手动终点必须明确是显式空图演示,不能代表现场无障碍;当前 AMR 位姿为车辆几何中心,输入在 UI 边界从 mm/deg 转为 m/rad。
|
||||
- 使用中文说明、目录树、数据流图、参数表、代码示例和常见错误表,保持 Map README 的信息密度与顺序。
|
||||
|
||||
## 验证
|
||||
|
||||
- 扩展 `ClumsyPilot/tests/verify_coarse_path_ui.ps1`,以 ASCII 稳定标识检查 README 含有新的主要章节、核心门面、数据流、P1 入口、单位、停止语义、非部分路径和限制边界。
|
||||
- 运行 README 的 P1 UI 检查,以及现有 Debug 构建和粗路径集成检查;文档改动不应影响生产代码或 P0 行为。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不重写或迁移 `Map/README.md`。
|
||||
- 不新增、删除或改名 C# 类型、场景、MovementTest 或测试脚本。
|
||||
- 不恢复、清理或迁移 TrapMap 及其旧验证脚本。
|
||||
@@ -0,0 +1,71 @@
|
||||
# P1 手动障碍物输入设计
|
||||
|
||||
## 目标
|
||||
|
||||
扩展 `[MovementTest(name = "粗路径规划")]`,使操作者可在一次手动测试中输入多个圆形或轴对齐矩形障碍物的中心与尺寸。输入经纯场景工厂转换为 `ManualObstacleSource`,再由已有 `CoarsePathPlanningService` 创建地图和规划;不绕过门面,也不添加任何底盘控制。
|
||||
|
||||
## 输入流程
|
||||
|
||||
1. 读取一次 `DetourInterface.getCartLocation()`,冻结 AMR 车身几何中心的世界 `X/Y(mm)` 与 `th(deg)`。
|
||||
2. 输入目标世界 `X(mm)`、`Y(mm)` 与航向 `deg`。
|
||||
3. 输入障碍物数量,允许范围为 `0` 到 `20`。
|
||||
4. 对每个障碍物输入类型:`1` 为圆形,`2` 为矩形。
|
||||
5. 输入障碍物几何中心世界 `X(mm)`、`Y(mm)`:
|
||||
- 圆形再输入半径 `r(mm)`;
|
||||
- 矩形再输入 X 方向长度与 Y 方向宽度(均为 mm)。矩形不提供旋转角,始终与世界坐标轴平行。
|
||||
6. 将已冻结的 AMR 位姿、目标和障碍物集合提交给现有后台执行器。
|
||||
|
||||
输入必须是有限数字。数量、类型、半径、长度和宽度不合法时拒绝启动规划并显示输入失败信息;不会产生不完整的规划请求。
|
||||
|
||||
## 工厂契约
|
||||
|
||||
`CoarsePathScenarioFactory` 新增面向手动测试的纯数据类型与工厂方法:
|
||||
|
||||
```csharp
|
||||
public enum ManualCoarsePathObstacleKind
|
||||
{
|
||||
Circle,
|
||||
AxisAlignedRectangle,
|
||||
}
|
||||
|
||||
public sealed class ManualCoarsePathObstacle
|
||||
{
|
||||
public static ManualCoarsePathObstacle Circle(
|
||||
double centerXMillimeters, double centerYMillimeters, double radiusMillimeters);
|
||||
|
||||
public static ManualCoarsePathObstacle AxisAlignedRectangle(
|
||||
double centerXMillimeters, double centerYMillimeters,
|
||||
double lengthXMillimeters, double widthYMillimeters);
|
||||
}
|
||||
|
||||
public static CoarsePathPlanningJob CreateManualObstacleDemo(
|
||||
double startXMillimeters, double startYMillimeters, double startHeadingDegrees,
|
||||
double goalXMillimeters, double goalYMillimeters, double goalHeadingDegrees,
|
||||
IReadOnlyList<ManualCoarsePathObstacle> obstacles, long obstacleSnapshotVersion);
|
||||
```
|
||||
|
||||
原有 `CreateManualGoalDemo` 保留不变,并委托到相同的边界/位姿转换逻辑和空障碍物路径,因此既有调用方与验证不受破坏。
|
||||
|
||||
工厂将中心/尺寸转换为 `CircleObstacle` 或 `AxisAlignedRectangleObstacle`。有障碍物时请求使用必需的 `ManualObstacleSource("manual-user-input", obstacleSnapshotVersion, true, ...)` 且 `AllowExplicitEmptyMap=false`;没有障碍物时使用空来源数组和 `AllowExplicitEmptyMap=true`。工厂校验障碍物列表、版本、几何数值与正尺寸,避免将缓存版本或无效几何交给 Map。
|
||||
|
||||
## 边界和缓存
|
||||
|
||||
手动地图边界的候选范围由起点、终点和每个障碍物的完整外轮廓共同决定:圆形使用中心 ± 半径,矩形使用中心 ± 半长/半宽。候选范围的每侧保留 2000 mm,随后按既有 50 mm 分辨率向外取整。
|
||||
|
||||
后台执行器为每次包含障碍物的手动提交生成单调递增的 `obstacleSnapshotVersion`,并在 UI 线程完成输入后冻结它。这样新输入绝不会复用旧障碍物地图;固定场景的缓存命中测试保持原样。空障碍物演示不需要障碍物来源版本。
|
||||
|
||||
## 可视化与停止
|
||||
|
||||
不新增 Painter 专用绘图分支。现有结果绘制已从 `PlanningGridMap.IsOccupied(row, col)` 消费占据格,因此新的手动障碍物会自动在同一 `CoarsePathPlanningV1` 图层显示为实际栅格快照。起点、终点、路径、换向、扩大车体检查框、状态和 `TestStop` 取消语义均保持不变。
|
||||
|
||||
## 验证与文档
|
||||
|
||||
- `verify_coarse_path_integration.ps1` 增加工厂反射与行为断言:新类型/方法存在;圆形和矩形输入生成非空手动来源、关闭显式空图、边界覆盖外轮廓;空障碍物仍保留显式空图;无效尺寸被拒绝。
|
||||
- `verify_coarse_path_ui.ps1` 检查手动入口读取障碍物数量、类型、中心和尺寸,并调用 `CreateManualObstacleDemo`,同时仍不直接创建地图或搜索器。
|
||||
- `CoarsePath/README.md` 的 P1 节说明输入顺序、单位、20 个上限、矩形无旋转、空图仅限零障碍物演示,以及可视化仍基于最终规划快照。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不支持旋转矩形、多边形、导入文件、拖拽编辑或实时编辑已运行任务。
|
||||
- 不让手动障碍物直接跳过 `ManualObstacleSource`、Map 缓存或 `CoarsePathPlanningService`。
|
||||
- 不改变已有固定场景、车辆安全参数、搜索算法、P1 Painter 颜色或任何底盘控制边界。
|
||||
@@ -0,0 +1,38 @@
|
||||
# Path Smoothing Six-Figure Report Design
|
||||
|
||||
## Goal
|
||||
|
||||
Replace the current one-file, three-panel path-smoothing report with six focused, independent point-plot figures for every scenario. Preserve both SVG and 600 dpi PNG export, retain one CSV metrics file, and never connect trajectory samples with lines.
|
||||
|
||||
## Output contract
|
||||
|
||||
Every scenario directory contains exactly these six figures in both `.svg` and `.png` form:
|
||||
|
||||
1. `01-coarse-path-overview`: raw Hybrid A* samples, map obstacles, start and coarse-path endpoint.
|
||||
2. `02-all-paths-comparison`: raw, B-spline, Bézier, and quintic samples together; no obstacles, start, or goal marker.
|
||||
3. `03-cubic-bspline-overview`: faded raw samples, B-spline samples, relevant obstacles, start and endpoint.
|
||||
4. `04-local-cubic-bezier-overview`: faded raw samples, Bézier samples, relevant obstacles, start and endpoint.
|
||||
5. `05-piecewise-quintic-overview`: faded raw samples, quintic samples, relevant obstacles, start and endpoint.
|
||||
6. `06-curvature-comparison`: raw and every available smoother's vehicle-curvature samples against arc length.
|
||||
|
||||
`comparison.csv` remains the single numerical report. The legacy composite `comparison.svg` and `comparison.png` are no longer emitted.
|
||||
|
||||
## Point-only rendering
|
||||
|
||||
Each `SmoothingFigurePoint` in a displayed series becomes one circular marker. SVG must not emit a trajectory polyline/path for any figure; PNG must not call a line-drawing API for trajectory samples. Marker size is fixed in report points so 0.025 m samples remain individually visible at 600 dpi. Start and endpoint remain distinct point markers only in figures 1, 3, 4, and 5.
|
||||
|
||||
Raw samples are dark gray, B-spline samples blue, Bézier samples orange, and quintic samples green. A method with no geometry has no markers but remains represented by an `Infeasible` or `Failed` status in that figure's legend.
|
||||
|
||||
## Framing and annotation
|
||||
|
||||
Every overhead figure derives its world bounds from the displayed path samples, then adds a fixed 10% padding with a 0.25 m minimum. The X/Y scales are equal. Obstacles are clipped by the panel rather than expanding the camera away from the path. Overhead axes show numeric ticks plus `X (m)` and `Y (m)` labels.
|
||||
|
||||
The curvature figure uses `s (m)` horizontally and `κ (m⁻¹)` vertically, with numeric ticks, zero axis, and displayed curvature limits. Every figure owns a compact legend describing its visible series and statuses.
|
||||
|
||||
## Export and compatibility
|
||||
|
||||
The existing shared, immutable comparison data remains the source of all six figures. SVG continues to use SimSun/Times New Roman family references. PNG continues to require exact SimSun and Times New Roman and returns `FontUnavailable` rather than falling back. The report exporter publishes all image files atomically and cleans any temporary files if one fails.
|
||||
|
||||
## Validation
|
||||
|
||||
Regression coverage verifies the six stable file stems, absence of trajectory line commands/styles, presence of all expected point markers and units, correct legends/statuses, valid PNG signature/CRC/600 dpi metadata, and no leftover `.tmp` files. Visual inspection covers straight, rectangle-detour, forward-reverse-switch, and an infeasible scenario.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Local G2 日报式报告交付设计
|
||||
|
||||
## 目标
|
||||
|
||||
在仓库根目录新增 `dailywork_report/`,交付两份中文主报告及各自独立、可直接打开的 HTML 可视化附录。内容面向研发人员和需要快速理解进度/风险的项目协作者。
|
||||
|
||||
## 交付物
|
||||
|
||||
```text
|
||||
dailywork_report/
|
||||
├── Map_rep/ # 预留:地图模块报告
|
||||
├── coarsepath_rep/ # 预留:粗路径模块报告
|
||||
└── pathsmoothing_rep/ # 本次 Local G2 报告
|
||||
├── 01-local-g2-quintic-hermite-algorithm-report.md
|
||||
├── 01-local-g2-quintic-hermite-algorithm-visualization.html
|
||||
├── 02-local-g2-issues-and-next-actions-report.md
|
||||
└── 02-local-g2-issues-and-next-actions-visualization.html
|
||||
```
|
||||
|
||||
HTML 文件为单文件附件:内嵌 CSS、SVG 和少量原生 JavaScript,不依赖网络、第三方 CDN 或构建步骤。
|
||||
|
||||
本次只在 `pathsmoothing_rep/` 中创建内容;`Map_rep/` 和 `coarsepath_rep/` 仅建立目录结构,供后续对应模块的日报式报告使用。
|
||||
|
||||
## 报告一:算法说明
|
||||
|
||||
主题为“Local G2 五次 Hermite 路径平滑算法说明”。内容按以下顺序组织:
|
||||
|
||||
1. 目标、适用位置与非目标:说明它位于 Hybrid A* 与后续 SQP 之间,只生成空间路径初值,不涉及速度、加速度或 SQP 求解。
|
||||
2. 输入:成功的粗路径、方向段、地图、车辆参数、G2 配置和取消令牌;明确坐标/单位与有效性前提。
|
||||
3. 模块架构:预处理、曲率跳变检测、窗口规划、五次 Hermite 候选构造、路径拼接、统一几何分析、安全/质量评价,以及计划中的专用发布流水线。
|
||||
4. 数据流:以“输入 → 检测 → 局部候选 → 安全筛选 → 输出”的线性流程说明每一步的职责和边界。
|
||||
5. 输出:说明路径点、方向段、曲率、曲率导数、区域报告、状态和诊断;明确当前任务 8 尚未接入,不能把候选层能力描述为已发布的主路径功能。
|
||||
6. 约束与安全门:不跨换向点、窗口/偏移/净空/曲率限制、候选数量上限和确定性排序。
|
||||
|
||||
配套 HTML 使用模块卡片、输入/输出栏和 SVG 数据流箭头,分别标明“已实现”和“待接入”模块。
|
||||
|
||||
## 报告二:问题分析与后续措施
|
||||
|
||||
主题为“Local G2 路径平滑问题分析与后续措施”。开头先给出证据边界:专项测试通过不等于端到端功能已经完成。随后按固定结构分别描述三个问题:
|
||||
|
||||
1. **已复现故障**:`RectangleDetour` 原始基线在平滑前的复验中变为 `InvalidInput`。
|
||||
2. **已确认的逻辑缺口**:窗口合并包络与候选总长度上限不一致,可能将本可分开处理的事件合并为没有合法候选的区域。
|
||||
3. **待验证的集成风险**:连续处理同一方向段多个区域时,前一处替换重算弧长可能让后一处继续使用旧的窗口坐标。
|
||||
|
||||
每个问题都包含:现象、通俗例子、技术成因、影响范围、证据等级、建议验证/修复措施和进入任务 8 前的验收条件。报告不把风险说成已经发生的运行时故障。
|
||||
|
||||
配套 HTML 使用状态徽章、场景示意 SVG、因果链和“问题 → 验证 → 措施”流程,突出已复现故障与待验证风险的区别。
|
||||
|
||||
## 写作与证据原则
|
||||
|
||||
- 以中文撰写,术语首次出现时同时给出白话解释。
|
||||
- 明确区分“已通过的专项测试”“已复现的失败”和“静态分析发现的风险”。
|
||||
- 引用现有实现计划、测试脚本、关键实现文件和本次实际测试结果;不宣称尚未实现的任务 8/9 已完成。
|
||||
- HTML 与 Markdown 的事实、术语和问题分级必须一致。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 四个文件均位于 `dailywork_report/`,命名稳定、无需外部资源即可阅读。
|
||||
- 两份 Markdown 报告结构完整,能够单独解释算法和问题。
|
||||
- 两份 HTML 附录在本地直接打开时内容可读、层级清楚、与主报告一致。
|
||||
- 报告二准确表达三个问题的证据等级和下一步,不给出未经验证的结论。
|
||||
- 本次工作只创建报告,不修改路径平滑算法或测试逻辑。
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
# Daily Summary Job 领域算法可视化升级设计
|
||||
|
||||
## 1. 目标
|
||||
|
||||
升级个人技能 `daily-summary-job`,使开发日报不只用文字和通用流程框陈述工作,而是先理解当天函数或算法的真实业务目标,再生成与该任务匹配的领域可视化。
|
||||
|
||||
页面必须让读者通过点击直接理解:
|
||||
|
||||
1. 原函数或算法解决什么问题、正常情况下如何工作;
|
||||
2. 当前问题发生在哪个对象、区域或算法阶段;
|
||||
3. 出错原因是什么,或当前有哪些待验证假设;
|
||||
4. 问题会沿什么路径传播并导致什么结果;
|
||||
5. 纠正方案会改变哪些对象、约束或处理步骤;
|
||||
6. 纠正后的预期效果是什么;
|
||||
7. 哪些内容是实际观测、静态分析、概念预演或已验证结果。
|
||||
|
||||
路径规划只是示例。技能必须根据当前任务选择合适的可视化,而不能把所有算法都硬编码成轨迹图或普通流程图。
|
||||
|
||||
## 2. 已确认的设计决策
|
||||
|
||||
- 使用混合生成策略:涉及函数或算法时自动生成基础领域视图;出现复杂问题时再生成深入诊断和修正前后对比。
|
||||
- 使用“双层算法地图”:正常算法效果作为稳定底图,问题与修正方案作为可切换叠加层。
|
||||
- 使用混合粒度:主图展示业务对象和算法阶段,点击后下钻到函数、源码、输入输出与约束。
|
||||
- 严格区分三类变化状态:当前故障、候选修正预演、已验证修正结果。
|
||||
- 使用“统一诊断外壳 + 领域可视化适配器”架构。
|
||||
- 测试与验证场景由当前任务、算法约束和问题类型动态决定,不使用预设的固定领域清单代替任务匹配。
|
||||
- 当前实施范围只交付通用声明式领域画布和统一诊断交互;路径规划、数值曲线、状态机等专用适配器在真实使用出现明确需求后再逐步增加。
|
||||
|
||||
## 3. 核心原则
|
||||
|
||||
### 3.1 领域效果优先
|
||||
|
||||
领域可视化必须展示算法实际处理的业务对象或结果:
|
||||
|
||||
- 空间或规划任务展示地图、边界、障碍物、姿态、搜索空间、候选路径或几何结果;
|
||||
- 数值任务展示真实曲线、阈值、异常区间、收敛过程或误差变化;
|
||||
- 搜索任务展示搜索空间、扩展顺序、代价变化、剪枝与最终路径;
|
||||
- 状态相关任务展示状态、迁移、触发条件、错误跳转和恢复路径;
|
||||
- 数据处理任务展示输入样本、中间变换、异常字段、影响传播与输出结果;
|
||||
- 其他任务展示最能表达其业务对象和正确性约束的视图。
|
||||
|
||||
当算法天然具有空间、数值、时间、状态或数据结构语义时,通用流程图只能作为辅助导航,不能代替主领域效果图。
|
||||
|
||||
### 3.2 先理解,后选择图形
|
||||
|
||||
技能不能仅根据目录名或函数名选择模板。生成可视化前必须回答:
|
||||
|
||||
- 当前函数或算法的业务目的是什么;
|
||||
- 输入、核心处理与输出是什么;
|
||||
- 用户需要直接观察的业务对象是什么;
|
||||
- 哪些约束决定结果是否正确;
|
||||
- 当前问题与哪个对象、区域或阶段关联;
|
||||
- 当前证据能支持展示哪些真实数据。
|
||||
|
||||
### 3.3 证据边界不可被动画掩盖
|
||||
|
||||
动画和交互只负责解释证据,不得制造证据。候选方案的预测画面必须明确标记为“概念预演”或“尚未验证”,不能显示成已经发生的修正结果。
|
||||
|
||||
## 4. 总体架构
|
||||
|
||||
```text
|
||||
当天对话、Agent 汇报、源码、测试和运行证据
|
||||
│
|
||||
▼
|
||||
任务与算法理解
|
||||
│
|
||||
▼
|
||||
Algorithm Visualization Brief
|
||||
│
|
||||
┌────────────┴────────────┐
|
||||
▼ ▼
|
||||
领域适配器选择 问题诊断关系构建
|
||||
│ │
|
||||
└────────────┬────────────┘
|
||||
▼
|
||||
领域主视图 + 统一诊断叠加层
|
||||
│
|
||||
▼
|
||||
Markdown / 交互式 HTML
|
||||
│
|
||||
▼
|
||||
任务匹配验证与证据一致性检查
|
||||
```
|
||||
|
||||
架构由五个逻辑组件组成。
|
||||
|
||||
### 4.1 任务与算法理解器
|
||||
|
||||
这是写入 `SKILL.md` 的 Agent 工作流程,不是只依赖关键词的确定性分类器。它负责:
|
||||
|
||||
1. 从当天证据中识别真正相关的函数、算法和业务任务;
|
||||
2. 合并主 Agent 与子 Agent 的成果、问题、原因、验证和遗留事项;
|
||||
3. 确定算法输入、输出、处理阶段、正确性约束和可观察对象;
|
||||
4. 区分实际数据、源码静态重建、对话结论与方案推演;
|
||||
5. 生成内部使用的可视化说明。
|
||||
|
||||
### 4.2 Algorithm Visualization Brief
|
||||
|
||||
可视化说明是技能内部生成的结构化事实,不要求用户手工填写。至少包含:
|
||||
|
||||
- 算法标识、名称、目的和领域语义;
|
||||
- 输入、输出、处理阶段与关键约束;
|
||||
- 适合的主视图类型和选择理由;
|
||||
- 可用的真实样本、运行数据及其来源;
|
||||
- 可视化对象与源码函数之间的关联;
|
||||
- 问题、影响、修正和预期结果关联到哪些图形对象;
|
||||
- 当前视图属于实际观测、静态重建、概念预演还是已验证结果。
|
||||
|
||||
### 4.3 领域可视化适配器
|
||||
|
||||
适配器负责把统一说明转换成领域主视图。适配器是可扩展能力,不是固定领域白名单。
|
||||
|
||||
当前版本提供可组合的声明式图元:
|
||||
|
||||
- 点、线、折线、曲线、区域、坐标轴和阈值;
|
||||
- 节点、边、树、图和搜索空间;
|
||||
- 状态、迁移、触发条件和时间线;
|
||||
- 网格、边界、障碍物、姿态和空间对象;
|
||||
- 输入输出样本、字段、数据块和转换关系;
|
||||
- 标注、告警、影响范围和证据引用。
|
||||
|
||||
当前版本由 Agent 根据可视化说明组合图元,形成符合任务语义的视图;适配器注册表只保留扩展接口。路径规划、数值曲线、状态机等专用适配器不属于本轮实施范围。若证据不足以形成可信领域视图,必须明确显示缺失信息,不得退化为伪装成实际效果的通用图。
|
||||
|
||||
### 4.4 统一诊断叠加层
|
||||
|
||||
所有领域视图共享相同的诊断交互协议。每个问题通过稳定问题 ID 和目标对象 ID 关联到主视图。
|
||||
|
||||
点击异常对象后必须展示:
|
||||
|
||||
- 目前状况;
|
||||
- 正常预期;
|
||||
- 出错位置;
|
||||
- 出错原因或待验证假设;
|
||||
- 影响传播路径;
|
||||
- 会导致的结果;
|
||||
- 纠正方案及步骤;
|
||||
- 纠正后的预期结果;
|
||||
- 实施和验证状态;
|
||||
- 函数、源码、测试和证据等级。
|
||||
|
||||
### 4.5 验证器
|
||||
|
||||
验证器继续检查 Markdown 与 HTML 的事实一致性和离线自包含性,并新增领域视图约束:
|
||||
|
||||
- 问题引用的算法、阶段和可视化对象必须存在;
|
||||
- 实际数值或几何结果必须具有证据引用;
|
||||
- 候选预演不能被标记成已验证结果;
|
||||
- 修正前后比较必须具有相同场景、单位和比较条件;
|
||||
- 每个可交互问题必须具有原因、影响、方案和预期结果;
|
||||
- 所有视图必须具有证据状态和必要的“概念示意”标签。
|
||||
|
||||
## 5. 报告事实结构扩展
|
||||
|
||||
现有 `achievements`、`issues`、`validations`、`next_steps` 和 `sources` 保持兼容,新增顶层 `algorithm_views`。
|
||||
|
||||
```json
|
||||
{
|
||||
"algorithm_views": [
|
||||
{
|
||||
"id": "planner-main",
|
||||
"name": "泊车路径规划",
|
||||
"purpose": "从起始姿态生成满足碰撞和运动学约束的可执行轨迹。",
|
||||
"domain": "spatial-planning",
|
||||
"adapter": "spatial-scene",
|
||||
"evidence_state": "actual",
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"constraints": [],
|
||||
"stages": [],
|
||||
"scene": {},
|
||||
"source_refs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`algorithm_views[].scene` 使用声明式数据,不直接嵌入任意脚本。具体适配器解释该字段并渲染 SVG、Canvas 或 DOM 图形。
|
||||
|
||||
每个 `issue` 新增:
|
||||
|
||||
- `algorithm_view_id`:关联的算法视图;
|
||||
- `target_ids`:主视图中需要高亮的对象;
|
||||
- `effect_target_ids`:影响传播涉及的对象;
|
||||
- `solution_preview`:候选修正会改变的对象和预期状态;
|
||||
- `verified_result`:存在真实修正验证时的结果引用。
|
||||
|
||||
旧报告缺少 `algorithm_views` 时仍可使用现有问题诊断页面,不得导致更新失败。
|
||||
|
||||
## 6. 页面交互结构
|
||||
|
||||
### 6.1 页面区域
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ 算法选择器 / 问题选择器 / 证据状态 │
|
||||
├───────────┬──────────────────────────┬───────────────────┤
|
||||
│ 查看模式 │ 领域效果主画面 │ 对象诊断卡 │
|
||||
│ │ │ │
|
||||
│ 正常机制 │ 路径、曲线、状态、搜索树 │ 目前状况 │
|
||||
│ 当前问题 │ 或其他任务匹配视图 │ 原因与影响 │
|
||||
│ 修正预演 │ │ 方案与预期 │
|
||||
│ 验证结果 │ │ 源码与测试证据 │
|
||||
├───────────┴──────────────────────────┴───────────────────┤
|
||||
│ 算法阶段导航 / 方案步骤 / 验证门 / 下一步 │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.2 四种查看模式
|
||||
|
||||
1. **正常机制**:展示算法原本的输入、处理、输出和正确性约束。
|
||||
2. **当前问题**:在正常底图上高亮异常对象、实际状态和影响传播。
|
||||
3. **修正预演**:逐步展示候选方案会改变什么,并明确标记尚未验证。
|
||||
4. **验证结果**:仅在存在修正后测试或运行证据时启用,展示真实结果及证据。
|
||||
|
||||
### 6.3 一次完整交互
|
||||
|
||||
```text
|
||||
选择算法
|
||||
→ 查看正常领域效果
|
||||
→ 选择问题或点击异常对象
|
||||
→ 播放原因与影响传播
|
||||
→ 点击纠正步骤查看候选变化
|
||||
→ 对比当前状态与预期状态
|
||||
→ 有验证证据时切换到已验证结果
|
||||
→ 下钻源码、测试和证据引用
|
||||
```
|
||||
|
||||
## 7. 任务匹配验证
|
||||
|
||||
报告生成时不得运行或引用与当前任务无关的固定测试场景。技能必须动态构建验证清单:
|
||||
|
||||
1. 识别当前任务和算法目标;
|
||||
2. 从源码、设计、测试和运行证据中提取正确性约束;
|
||||
3. 确定当前问题的复现条件和失败判据;
|
||||
4. 查找与这些条件直接匹配的现有测试或运行证据;
|
||||
5. 只在安全且成本合理时运行针对性验证;
|
||||
6. 将已运行、未运行和仍缺失的验证严格分开;
|
||||
7. 为没有匹配测试的结论生成任务专属验证建议。
|
||||
|
||||
示例:泊车规划任务可以匹配碰撞、安全间距、可达性、曲率和车辆运动学约束;并发缓存任务可以匹配竞争、重复写入、超时和一致性约束。这些示例用于说明匹配原则,不是固定覆盖列表。
|
||||
|
||||
## 8. 证据状态与视觉语义
|
||||
|
||||
| 状态 | 含义 | 页面表达 |
|
||||
| --- | --- | --- |
|
||||
| 实际观测 | 来自测试、运行或可复核数据 | 实线、明确数值和证据引用 |
|
||||
| 静态重建 | 根据源码控制流或公式重建 | 静态分析标签,不声称运行复现 |
|
||||
| 概念预演 | 候选方案的预测效果 | 虚线或半透明,并显示尚未验证 |
|
||||
| 已验证结果 | 修正后经过匹配测试确认 | 已验证标签和测试证据 |
|
||||
| 结论冲突 | 多个可信证据不一致 | 同时保留视图与结论,显示冲突状态 |
|
||||
|
||||
颜色不能成为唯一状态区分方式;同时使用文字、线型、图标和可访问标签。
|
||||
|
||||
## 9. 降级与错误处理
|
||||
|
||||
- 当天没有函数或算法工作:生成普通开发日报,不强制创建算法视图。
|
||||
- 找到算法但缺少运行数据:允许静态重建或概念示意,并明确证据状态。
|
||||
- 无法确认算法业务目的:列出缺失证据,不生成伪领域效果。
|
||||
- 多个算法同时出现:提供算法选择器,分别维护视图与问题关联。
|
||||
- 数据量过大:允许抽样、聚合或简化,页面必须说明简化规则并保留原始证据位置。
|
||||
- 适配器无法渲染某个图元:显示可读的局部错误卡,其他报告内容仍可访问。
|
||||
- 修正方案没有验证:禁用“已验证结果”模式,而不是复制候选预演内容。
|
||||
|
||||
## 10. 文件和组件变化
|
||||
|
||||
预计修改:
|
||||
|
||||
- `SKILL.md`:增加任务理解、可视化说明、领域适配器选择和任务匹配验证流程。
|
||||
- `references/report-schema.md`:增加 `algorithm_views`、问题对象关联和证据状态字段。
|
||||
- `scripts/prepare_report.py`:验证新结构、保持旧结构兼容、向模板注入领域视图数据。
|
||||
- `assets/interactive-report-template.html`:重构为统一诊断外壳和适配器注册表。
|
||||
- `scripts/test_prepare_report.py`:增加结构、适配器协议、交互和证据边界测试。
|
||||
|
||||
可以按复杂度把适配器拆入 `assets/visual-adapters/`,但最终报告仍必须是一个无外部依赖的 HTML 文件。
|
||||
|
||||
## 11. 验收标准
|
||||
|
||||
- 技能先识别任务目的和业务对象,再选择可视化,不按固定目录名盲选。
|
||||
- 路径、数值、状态、搜索或其他算法能够呈现各自真实领域效果,而不是统一文字流程框。
|
||||
- 点击图中异常对象可查看目前状况、原因、后果、纠正方案和预期结果。
|
||||
- 正常机制、当前问题、候选预演和已验证结果可以明确切换。
|
||||
- 候选方案在没有验证证据时不会显示为已修复。
|
||||
- 问题、图形对象、源码和测试证据能够互相追踪。
|
||||
- 验证清单根据当前任务动态生成,不以固定领域测试替代任务匹配。
|
||||
- 无法形成可信领域视图时诚实降级,不编造运行数据。
|
||||
- Markdown 与 HTML 保持事实一致,HTML 离线可用并支持键盘和移动端。
|
||||
- 旧日报数据仍可生成和更新。
|
||||
|
||||
## 12. 非目标
|
||||
|
||||
- 不在生成日报时自动修改业务代码。
|
||||
- 不为了可视化而运行昂贵、破坏性或未经授权的测试。
|
||||
- 不要求每种算法预先拥有专用硬编码模板。
|
||||
- 不把动画效果当作算法正确性的证明。
|
||||
- 不自动暂存、提交或推送 Git 变更。
|
||||
@@ -0,0 +1,188 @@
|
||||
# Daily Summary Job 个人技能设计
|
||||
|
||||
## 目标
|
||||
|
||||
创建个人技能 `daily-summary-job`,在用户按需要求记录进展、生成今日日报或更新今日日报时,整理当前开发工作的成果、问题发现、改善措施、验证状态和下一步,并生成事实一致的 Markdown 主报告与单文件交互式 HTML。
|
||||
|
||||
技能面向任意本地项目。项目缺少日报目录、模块目录或日期目录时,技能只初始化报告归档结构,不创建或修改业务代码目录。
|
||||
|
||||
## 名称与安装范围
|
||||
|
||||
- 规范技能名:`daily-summary-job`。
|
||||
- 界面显示名:`Daily Summary Job`。
|
||||
- 安装范围:个人技能目录 `$CODEX_HOME/skills/daily-summary-job`;`CODEX_HOME` 未设置时使用 `~/.codex/skills/daily-summary-job`。
|
||||
- `dailySummary_job` 只作为用户原始名称保留在说明中,不作为目录名或 YAML 名称。
|
||||
|
||||
## 按需触发
|
||||
|
||||
技能不后台运行,也不自动监听 Agent。以下是意图示例,不是固定口令:
|
||||
|
||||
- “记录当前进展”“把刚才的问题加入今日记录”进入检查点模式。
|
||||
- “生成今日日报”“汇总今天的开发工作”进入生成模式。
|
||||
- “更新今天的日报”“把刚解决的问题补充进去”进入更新模式。
|
||||
- 显式使用 `$daily-summary-job` 时最可靠;自然语言明确表达日报、今日问题整理或进展记录意图时也应触发。
|
||||
|
||||
## 工作流
|
||||
|
||||
```text
|
||||
当前对话与 Agent 汇报 ─┐
|
||||
Git 提交、改动与文档 ──┼─→ 结构化事实源 ─→ Markdown 主报告
|
||||
已有测试与构建结果 ────┘ └→ 交互式 HTML
|
||||
```
|
||||
|
||||
1. 确定项目根目录和项目机器的本地日期;用户可以覆盖日期。
|
||||
2. 从当前对话、主 Agent 与子 Agent 汇报中提取成果、问题、调查结论、改善和遗留事项。
|
||||
3. 用当天 Git 提交、未提交改动、设计/计划文档以及已有测试结果交叉核对。
|
||||
4. 将信息压缩为结构化事实源,并根据稳定问题标识去重。
|
||||
5. 自动识别单模块、多模块或无法分类的工作范围。
|
||||
6. 初始化缺失的报告、模块和日期目录。
|
||||
7. 由同一结构化事实源生成 Markdown 与 HTML,避免两者事实漂移。
|
||||
8. 更新模式合并新证据并重新生成原有文件对,不重复创建相同主题。
|
||||
|
||||
默认不重新运行耗时构建或测试。已有证据不足时标记“待验证”;完全没有有效开发证据时不生成空日报。
|
||||
|
||||
## 上下文预算
|
||||
|
||||
技能采用渐进式读取:
|
||||
|
||||
- `SKILL.md` 只保留核心流程和路由规则。
|
||||
- 先读取当天检查点索引,再加载相关模块的必要记录。
|
||||
- 不读取历史日期的日报,除非用户明确要求比较。
|
||||
- 检查点不复制完整对话或完整日志,只保存结论和证据引用。
|
||||
- 每次检查点最多记录 5 条成果、5 个问题和 3 个下一步;单条说明尽量不超过 120 个汉字。
|
||||
- 长日志只记录命令、文件路径、提交号、结果摘要和原始证据位置。
|
||||
|
||||
磁盘上的历史文件不会自动进入上下文;只有本次任务选中的文件才会读取。
|
||||
|
||||
## 证据模型
|
||||
|
||||
每条问题至少包含:
|
||||
|
||||
- 稳定标识、标题和所属模块;
|
||||
- 问题如何被发现、实际现象和正确预期;
|
||||
- 原因或当前假设、影响范围;
|
||||
- 已采取的改善、验证结果和下一步;
|
||||
- 证据引用与证据等级。
|
||||
|
||||
证据等级固定为:
|
||||
|
||||
| 等级 | 含义 |
|
||||
| --- | --- |
|
||||
| 已验证 | 有测试、构建、运行输出或可复核改动支持。 |
|
||||
| 静态分析 | 可从当前代码和控制流确认,但尚无运行复现。 |
|
||||
| 对话发现 | Agent 或用户在讨论中提出,尚未完成独立核验。 |
|
||||
| 待验证风险 | 合理推断,仍需要专门实验或回归。 |
|
||||
|
||||
多个 Agent 给出冲突结论时,不擅自合并为单一事实。结构化事实源保留冲突双方、各自证据和待验证动作,报告明确显示“结论冲突”。
|
||||
|
||||
## 自动分类与目录初始化
|
||||
|
||||
项目根目录优先使用 Git 根;没有 Git 时使用当前工作目录。分类顺序如下:
|
||||
|
||||
1. 用户明确指定的模块。
|
||||
2. 当天改动路径与既有 `dailywork_report/*_rep` 的匹配结果。
|
||||
3. 代码、测试和文档中占主导的业务目录。
|
||||
4. 涉及多个独立模块时使用 `cross-module_rep`。
|
||||
5. 无法可靠判断或项目没有代码目录时使用 `general_rep`。
|
||||
|
||||
既有项目命名优先,例如已有 `pathsmoothing_rep` 时不另建语义重复目录。新模块名只允许安全的小写字母、数字和连字符,再追加 `_rep`。
|
||||
|
||||
```text
|
||||
dailywork_report/
|
||||
├── .daily-summary-job/
|
||||
│ └── YYYY-MM-DD/
|
||||
│ └── checkpoints/
|
||||
│ └── HHmmss-<module>.json
|
||||
└── <module>_rep/
|
||||
└── YYYY-MM-DD/
|
||||
├── NN-<topic>-daily-summary-report.md
|
||||
└── NN-<topic>-daily-summary-visualization.html
|
||||
```
|
||||
|
||||
- 同日同主题更新原文件对。
|
||||
- 同日新主题从 `01` 开始递增编号。
|
||||
- 检查点目录保存精简、机器可读的中间证据;最终日报目录只保留交付文件。
|
||||
- 所有目录均按需创建;技能不创建任何业务源码目录。
|
||||
|
||||
## Markdown 报告
|
||||
|
||||
Markdown 使用固定主结构,但允许没有内容的非关键小节省略:
|
||||
|
||||
1. 今日结论摘要。
|
||||
2. 今日完成的工作。
|
||||
3. 今日发现的问题。
|
||||
4. 问题如何被发现及证据等级。
|
||||
5. 已采取的改善和验证结果。
|
||||
6. 尚未解决的风险与下一步。
|
||||
7. 变更、测试和资料证据索引。
|
||||
|
||||
问题描述采用“发现 → 现象 → 原因/假设 → 影响 → 改善 → 验证 → 下一步”的顺序。不得把未运行的测试写成通过,也不得把候选方案写成已完成修复。
|
||||
|
||||
## 交互式 HTML
|
||||
|
||||
每份 Markdown 对应一个单文件离线 HTML。HTML 内嵌 CSS、结构化数据和原生 JavaScript,不使用 CDN、网络请求、第三方库或外部图片。
|
||||
|
||||
页面信息流为:
|
||||
|
||||
```text
|
||||
今日总览
|
||||
↓ 选择问题
|
||||
现象与正确预期对照
|
||||
↓ 展开因果节点
|
||||
发现过程 → 证据 → 根因/风险
|
||||
↓ 切换改善步骤
|
||||
修改前 → 改善措施 → 修改后
|
||||
↓ 查看验证门
|
||||
测试结果 → 遗留风险 → 下一步
|
||||
```
|
||||
|
||||
交互组件包括:
|
||||
|
||||
- 成果、问题、验证和待办总览;
|
||||
- 问题选择器与证据等级筛选;
|
||||
- “实际发生 / 正确预期”对照;
|
||||
- 可展开的发现与因果链;
|
||||
- 改善方案步骤导航和修改前后切换;
|
||||
- 构建、测试、安全约束等验证门漏斗;
|
||||
- 按优先级和模块筛选的下一步路线图;
|
||||
- 键盘导航、移动端布局与 `prefers-reduced-motion` 支持。
|
||||
|
||||
没有数值证据时只使用明确标注的概念图,不伪造曲线、比例或指标。HTML 与 Markdown 必须由同一份规范化 JSON 生成。
|
||||
|
||||
## 技能组成
|
||||
|
||||
```text
|
||||
daily-summary-job/
|
||||
├── SKILL.md
|
||||
├── agents/openai.yaml
|
||||
├── scripts/prepare_report.py
|
||||
├── scripts/test_prepare_report.py
|
||||
├── references/report-schema.md
|
||||
└── assets/interactive-report-template.html
|
||||
```
|
||||
|
||||
- `SKILL.md`:触发、取证、分类、生成和更新流程。
|
||||
- `agents/openai.yaml`:显示名、简短说明和默认提示。
|
||||
- `scripts/prepare_report.py`:安全规范化名称、选择输出路径、生成 Markdown/HTML 并执行一致性校验。
|
||||
- `scripts/test_prepare_report.py`:使用 Python 标准库验证路径、分类、预算、生成和更新行为。
|
||||
- `references/report-schema.md`:结构化事实源字段、证据等级和内容约束。
|
||||
- `assets/interactive-report-template.html`:响应式、无外部依赖的交互页面模板。
|
||||
|
||||
## 异常与安全边界
|
||||
|
||||
- 目标文件存在且无法确认同一主题时,创建新编号,不覆盖未知内容。
|
||||
- 更新前校验结构化事实源和目标文件配对关系。
|
||||
- 生成先写入临时文件并校验,成功后再替换文件对;失败时保留已有有效版本。
|
||||
- 路径、模块和主题统一安全规范化,拒绝目录穿越。
|
||||
- HTML 中的所有项目文本进行转义,避免把代码或对话内容解释为页面脚本。
|
||||
- 技能只整理和生成日报;不修复业务代码、不放宽测试或安全门,也不执行 Git 提交。
|
||||
|
||||
## 验证标准
|
||||
|
||||
- 技能目录通过 `quick_validate.py`。
|
||||
- 路径脚本覆盖 Git/非 Git、单模块、多模块、无模块、非法名称、同主题更新和连续编号。
|
||||
- 模拟项目完成一次“记录 → 生成 → 更新”流程。
|
||||
- Markdown 与 HTML 包含相同问题标识、证据等级、改善和下一步。
|
||||
- HTML 不包含外部 URL、外部脚本或第三方依赖。
|
||||
- 交互控件、键盘操作、响应式规则和减少动画规则均存在。
|
||||
- 全部验证不修改 ParkingRobot 的业务源码,也不暂存或提交任何文件。
|
||||
Reference in New Issue
Block a user