13 KiB
Workstation-Bounded Layered Trap Map Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Build a task-level world-coordinate grid bounded by the current vehicle, an adjustable workstation, and independent X/Y margins; tire detection remains an optional layer.
Architecture: TrapMapBuilder validates required inputs, reads the Detour pose, calculates the vehicle/workstation rectangle, creates the base grid, and marks the self layer before attempting tire detection. Tire acquisition records layer health and optionally adds occupancy without controlling map success. GridMapData remains the merged output so a future point-cloud layer can rasterize into the same map through a separate step.
Tech Stack: C# 10, .NET Standard 2.0, ClumsyCore/ClumsyDance, PowerShell reflection/contract tests, dotnet msbuild.
Global Constraints
- Do not inspect or modify
TrajPlanner. - Workstation coordinates use the Detour world frame and millimetres.
- Defaults:
WorkstationX=10000f,WorkstationY=0f,MapMarginX=3000f,MapMarginY=3000f. Freemeans “not marked by an integrated source,” not sensor-confirmed obstacle-free.- Do not invent a point-cloud API or send chassis commands.
- Reject maps above
4_000_000cells before allocatingbyte[,].
Task 1: Executable task-bound calculation
Files:
- Modify:
ClumsyPilot/MovementTest.Trapmaptest.cs - Modify:
ClumsyPilot/tests/verify_trapmap_grid.ps1
Interfaces:
-
Consumes: vehicle/workstation coordinates, X/Y margins, resolution, cell limit.
-
Produces:
TrapMapBounds.TryCreate(float carX, float carY, float workstationX, float workstationY, float marginX, float marginY, float resolutionMm, int maxCellCount, out TrapMapBounds bounds, out string failureReason)plusXMin,XMax,YMin,YMax,Rows,Cols,CellCount. -
Step 1: Write failing reflection tests
Append to verify_trapmap_grid.ps1:
$boundsType = $assembly.GetType('MultiWheelC.TrapMapBounds', $true)
$tryCreate = $boundsType.GetMethod('TryCreate')
function Invoke-Bounds([single]$carX, [single]$carY, [single]$stationX, [single]$stationY,
[single]$marginX, [single]$marginY, [single]$resolution, [int]$maxCells) {
$args = @($carX, $carY, $stationX, $stationY, $marginX, $marginY,
$resolution, $maxCells, $null, $null)
$ok = $tryCreate.Invoke($null, $args)
[pscustomobject]@{ Ok=$ok; Bounds=$args[8]; Reason=$args[9] }
}
$result = Invoke-Bounds 2000 -1000 10000 0 3000 4000 50 4000000
Assert-Equal $true $result.Ok 'Valid bounds must succeed.'
Assert-Equal ([single]-1000) $result.Bounds.XMin 'Wrong XMin.'
Assert-Equal ([single]13000) $result.Bounds.XMax 'Wrong XMax.'
Assert-Equal ([single]-5000) $result.Bounds.YMin 'Wrong YMin.'
Assert-Equal ([single]4000) $result.Bounds.YMax 'Wrong YMax.'
Assert-Equal 280 $result.Bounds.Cols 'Wrong column count.'
Assert-Equal 180 $result.Bounds.Rows 'Wrong row count.'
Assert-Equal $false (Invoke-Bounds 0 0 100000 100000 0 0 20 4000000).Ok 'Oversized map must fail.'
Assert-Equal $false (Invoke-Bounds 0 0 10000 0 -1 3000 50 4000000).Ok 'Negative margin must fail.'
- Step 2: Prove the new test fails
Run:
dotnet msbuild ClumsyPilot\ClumsyPilot.csproj /t:Compile /p:RestoreIgnoreFailedSources=true /v:minimal
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_grid.ps1
Expected: compile succeeds; the script fails because MultiWheelC.TrapMapBounds is absent.
- Step 3: Implement the bounds object
Add TrapMapBounds beside the input models. TryCreate must validate every float with TrapMapValue.IsFinite, require margins >=0, resolution and limit >0, calculate:
float xMin = Math.Min(carX, workstationX) - marginX;
float xMax = Math.Max(carX, workstationX) + marginX;
float yMin = Math.Min(carY, workstationY) - marginY;
float yMax = Math.Max(carY, workstationY) + marginY;
int cols = (int)Math.Ceiling((xMax - xMin) / resolutionMm);
int rows = (int)Math.Ceiling((yMax - yMin) / resolutionMm);
long cellCount = (long)rows * cols;
Reject non-finite/degenerate boundaries and cellCount > maxCellCount before returning a populated immutable-result object. Return a concrete Chinese reason for every rejection.
- Step 4: Recompile and run behavior tests
Run the Step 2 commands. Expected final line: TrapMap GridMapData behavior checks passed.
- Step 5: Commit
git add ClumsyPilot/MovementTest.Trapmaptest.cs ClumsyPilot/tests/verify_trapmap_grid.ps1
git commit -m "feat: calculate workstation-bounded trap map"
Task 2: Base-map-first lifecycle and optional tire layer
Files:
- Modify:
ClumsyPilot/MovementTest.Trapmaptest.cs - Modify:
ClumsyPilot/tests/verify_trapmap_inputs.ps1
Interfaces:
-
Consumes: Task 1
TrapMapBounds.TryCreatewith its declared ten parameters, Detour pose, car dimensions, optionalTwoLegDetectoutput. -
Produces:
WorkstationX,WorkstationY,MapMarginX,MapMarginY,WorkstationWorld,TrapMapTireLayerStatus TireLayerStatus, andstring TireLayerMessage. -
Step 1: Add failing lifecycle contracts
Add to verify_trapmap_inputs.ps1:
if ($source -match '\bMapHalfSizeMm\b') { $failures.Add('Legacy MapHalfSizeMm remains.') }
foreach ($name in 'WorkstationX','WorkstationY','MapMarginX','MapMarginY','TrapMapBounds','TireLayerStatus','TireLayerMessage') {
if ($source -notmatch "\b$name\b") { $failures.Add("Missing layered-map member: $name") }
}
if ($source -notmatch 'WorkstationX\s*=\s*10000f' -or $source -notmatch 'WorkstationY\s*=\s*0f' -or
$source -notmatch 'MapMarginX\s*=\s*3000f' -or $source -notmatch 'MapMarginY\s*=\s*3000f') {
$failures.Add('Required task-map defaults are missing.')
}
$createIndex = $source.IndexOf('TryCreateGridMap(')
$detectIndex = $source.IndexOf('ReadTireLayer(')
if ($createIndex -lt 0 -or $detectIndex -lt 0 -or $createIndex -gt $detectIndex) {
$failures.Add('Base map must precede optional tire acquisition.')
}
if ($source -notmatch 'TRAPMAP_FREE_SEMANTICS') { $failures.Add('Free semantics are undocumented.') }
- Step 2: Run the contract and confirm failure
Run powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_inputs.ps1.
Expected: legacy half-size, missing members, and ordering assertions fail.
- Step 3: Add task-map configuration and status types
Replace MapHalfSizeMm with:
public float WorkstationX { get; set; } = 10000f;
public float WorkstationY { get; set; } = 0f;
public float MapMarginX { get; set; } = 3000f;
public float MapMarginY { get; set; } = 3000f;
public const int MaxCellCount = 4_000_000;
public Vector2 WorkstationWorld { get; private set; }
public TrapMapTireLayerStatus TireLayerStatus { get; private set; }
public string TireLayerMessage { get; private set; }
Define enum values NotAttempted, Populated, NoDetection, Unavailable, Simulated. Validate finite workstation coordinates and non-negative finite margins.
- Step 4: Reorder
Get()and split population
Implement this required sequence:
VehiclePose = vehiclePose;
WorkstationWorld = new Vector2(WorkstationX, WorkstationY);
if (!TryCreateGridMap(out var gridMap, out failureReason) ||
!TryPopulateSelfLayer(gridMap, out failureReason)) {
Fail(failureReason);
yield break;
}
ReadTireLayer(self);
TryPopulateTireLayer(gridMap);
TryCreateGridMap calls Task 1's ten-parameter TrapMapBounds.TryCreate and constructs GridMapData from the returned limits. The self method marks only the vehicle. Tire population may log a rasterization problem but must not clear or suppress the valid base map.
- Step 5: Make tire acquisition non-blocking
Rename the required-input method to ReadTireLayer. Every exit assigns an empty or populated list plus status/message. Use:
private void SetEmptyTireLayer(TrapMapTireLayerStatus status, string message)
{
DetectedObstacles = new List<TrapMapObstacle>();
TireLayerStatus = status;
TireLayerMessage = message;
InputSource = "无轮胎障碍输入";
DLog.Log($"轮胎层为空: status={status}, reason={message}", "TrapMapTest");
}
Real-mode empty lidar/config errors/exceptions/illegal coordinates become Unavailable; detected == null becomes NoDetection; valid endpoints become Populated. Ghost simulation remains Simulated and is never a real-mode fallback.
Add beside base-map creation:
// TRAPMAP_FREE_SEMANTICS: 当前 Free 仅表示尚未被已接入层标记,
// 不表示传感器确认现实中无障碍。未来实时点云应作为独立障碍层;
// 引入 Unknown 后,未观测栅格不应默认允许规划通行。
- Step 6: Verify and commit
Run powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_inputs.ps1, dotnet msbuild ClumsyPilot\ClumsyPilot.csproj /t:Compile /p:RestoreIgnoreFailedSources=true /v:minimal, and powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_grid.ps1. Expected: both scripts exit 0 and compile has no TrapMap errors. Then:
git add ClumsyPilot/MovementTest.Trapmaptest.cs ClumsyPilot/tests/verify_trapmap_inputs.ps1
git commit -m "fix: build trap map before optional tire layer"
Task 3: Adjustable test entry and workstation visualization
Files:
- Modify:
ClumsyPilot/MovementTest.Trapmaptest.cs - Modify:
ClumsyPilot/tests/verify_trapmap_inputs.ps1
Interfaces:
-
Consumes: Task 2 builder configuration/status.
-
Produces: editable test defaults, non-occupying target marker, exact bounds/layer logs.
-
Step 1: Add failing UI/source contracts
if ($source -notmatch '_workstationX\s*=\s*10000f' -or $source -notmatch '_workstationY\s*=\s*0f' -or
$source -notmatch '_mapMarginX\s*=\s*3000f' -or $source -notmatch '_mapMarginY\s*=\s*3000f') {
$failures.Add('MovementTest adjustable defaults are missing.')
}
if ($source -notmatch 'DrawWorkstationMarker\(') { $failures.Add('Target visualization is missing.') }
if ($source -match 'MarkObstacle\(Workstation|MarkOccupied\([^\r\n]*Workstation') {
$failures.Add('Workstation must not become occupancy.')
}
- Step 2: Prove the contract fails
Run the input script. Expected: missing test defaults and target visualization are reported.
- Step 3: Replace manual-edit fields and pass them to builder
private const float _gridResolutionMm = 50f;
private const float _workstationX = 10000f;
private const float _workstationY = 0f;
private const float _mapMarginX = 3000f;
private const float _mapMarginY = 3000f;
private const float _safetyMarginMm = 300f;
Pass all values into TrapMapBuilder. Log vehicle, workstation, limits, dimensions, cells, TireLayerStatus, and TireLayerMessage. Remove all half-size/20m-window documentation.
- Step 4: Draw the workstation without marking occupancy
private void DrawWorkstationMarker(Painter painter)
{
const float radius = 250f;
painter.DrawCircle(Color.Lime, WorkstationWorld.X, WorkstationWorld.Y, radius);
painter.DrawLine(Color.Lime, WorkstationWorld.X - radius, WorkstationWorld.Y,
WorkstationWorld.X + radius, WorkstationWorld.Y, width: 3);
painter.DrawLine(Color.Lime, WorkstationWorld.X, WorkstationWorld.Y - radius,
WorkstationWorld.X, WorkstationWorld.Y + radius, width: 3);
painter.DrawText(Color.Lime, "Workstation",
WorkstationWorld.X + radius, WorkstationWorld.Y + radius);
}
Call it from visualization; never pass WorkstationWorld to occupancy methods.
- Step 5: Update comments and run final verification
Document formulas, defaults, task-level “global” meaning, layer order, optional tire behavior, and future independent point-cloud layer. Run:
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_inputs.ps1
dotnet msbuild ClumsyPilot\ClumsyPilot.csproj /t:Compile /p:RestoreIgnoreFailedSources=true /v:minimal
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_grid.ps1
rg -n "MapHalfSizeMm|GetSensor\(|GetPointCloud\(|PredefinedDriveStop\(|SendMotion\(|SendRotateMotion\(" ClumsyPilot\MovementTest.Trapmaptest.cs
git diff --check
Expected: scripts pass, compile has no TrapMap errors, forbidden search has no matches, and diff check is clean.
- Step 6: Commit
git add ClumsyPilot/MovementTest.Trapmaptest.cs ClumsyPilot/tests/verify_trapmap_inputs.ps1
git commit -m "feat: expose workstation trap map inputs"