558 lines
24 KiB
Markdown
558 lines
24 KiB
Markdown
# EM Planner Foundation 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 the immutable EM Planner contracts, validated configuration, direction-segment boundary model, reverse-safe Frenet transforms, and topology-preserving static lateral corridor.
|
|
|
|
**Architecture:** Keep the planner core request-driven and independent from hardware, UI, clocks, and solver details. Adapt one consumable `PathSmoothingResult` into an immutable current-direction reference window, then project only within that segment and build a collision-checked connected lateral interval around the selected seed.
|
|
|
|
**Tech Stack:** C# 10, .NET Standard 2.0 library, existing `PlanningGridMap`, `FootprintCollisionChecker`, and a `net10.0-windows` console verification host.
|
|
|
|
## Global Constraints
|
|
|
|
- Namespace for new production types: `MultiWheelC.TrajectoryPlanning.EMPlanner` plus responsibility-specific child namespaces.
|
|
- First release supports ordinary nonholonomic forward/reverse motion only; lateral translation, crab motion, and in-place rotation are rejected.
|
|
- World units are metres, seconds, radians, metres/second, radians/second, and inverse metres.
|
|
- Vehicle body X points forward, body Y points left, and positive yaw is counter-clockwise.
|
|
- `TravelYaw = VehicleYaw` forward and `TravelYaw = Normalize(VehicleYaw + π)` reverse.
|
|
- Frenet `s` always increases in actual travel direction; positive `l` is left of travel, including reverse.
|
|
- A planning window never crosses a gear-switch boundary and never drops an exact terminal anchor.
|
|
- Static-corridor selection preserves the topology chosen by Hybrid A*; it never jumps to another disconnected free interval.
|
|
- Initial corridor spacings are `0.10 m` longitudinal and `0.025 m` lateral; maximum lateral offset is `0.30 m`.
|
|
- Existing user changes in `ClumsyPilot.csproj`, Map, CoarsePath, and PathSmoothing must be preserved.
|
|
- Dynamic-obstacle trajectory prediction and dynamic space-time boundaries are outside this plan.
|
|
|
|
---
|
|
|
|
## Locked File Structure
|
|
|
|
```text
|
|
ClumsyPilot/ParkrobTrajplanner/EMPlanner/
|
|
├── Contracts/
|
|
│ ├── EmBoundaryType.cs
|
|
│ ├── EmMotionModel.cs
|
|
│ ├── EmPlanningRequest.cs
|
|
│ ├── EmPlanningResult.cs
|
|
│ ├── EmPlanningStatus.cs
|
|
│ ├── EmTerminalType.cs
|
|
│ ├── EmTrajectory.cs
|
|
│ ├── EmTrajectoryMetadata.cs
|
|
│ ├── EmTrajectoryPoint.cs
|
|
│ └── VehicleMotionState.cs
|
|
├── Configuration/
|
|
│ ├── CorridorConfiguration.cs
|
|
│ ├── EmPlannerConfiguration.cs
|
|
│ ├── FrenetConfiguration.cs
|
|
│ ├── LateralConfiguration.cs
|
|
│ ├── LateralWeights.cs
|
|
│ ├── LongitudinalConfiguration.cs
|
|
│ ├── LongitudinalWeights.cs
|
|
│ ├── SchedulingConfiguration.cs
|
|
│ ├── SolverConfiguration.cs
|
|
│ └── ValidationConfiguration.cs
|
|
├── Diagnostics/
|
|
│ ├── EmPlannerDebugOptions.cs
|
|
│ ├── EmPlanningDiagnostics.cs
|
|
│ └── IEmPlannerDebugSink.cs
|
|
├── Segmentation/
|
|
│ ├── DirectionSegmentView.cs
|
|
│ ├── ReferenceBoundary.cs
|
|
│ ├── ReferenceHorizonSlicer.cs
|
|
│ └── ReferencePathSegmenter.cs
|
|
├── Frenet/
|
|
│ ├── FrenetProjection.cs
|
|
│ ├── FrenetProjector.cs
|
|
│ ├── FrenetReferencePoint.cs
|
|
│ ├── FrenetTransform.cs
|
|
│ └── ReferencePathInterpolator.cs
|
|
├── Corridor/
|
|
│ ├── LateralInterval.cs
|
|
│ ├── StaticCorridor.cs
|
|
│ └── StaticCorridorBuilder.cs
|
|
└── Validation/
|
|
└── EmPlanningRequestValidator.cs
|
|
|
|
ClumsyPilot/tests/EMPlannerVerificationHost/
|
|
├── EMPlannerVerificationHost.csproj
|
|
├── Program.cs
|
|
├── Verification.cs
|
|
├── EmFixtureFactory.cs
|
|
├── FoundationChecks.cs
|
|
├── SegmentationChecks.cs
|
|
├── FrenetChecks.cs
|
|
└── CorridorChecks.cs
|
|
```
|
|
|
|
## Shared Public Interfaces
|
|
|
|
```csharp
|
|
public sealed class VehicleMotionState
|
|
{
|
|
public VehicleMotionState(Pose2D pose, double signedLongitudinalSpeedMetersPerSecond,
|
|
double? longitudinalAccelerationMetersPerSecondSquared,
|
|
DateTimeOffset capturedAtUtc, long sequenceId);
|
|
public Pose2D Pose { get; }
|
|
public double SignedLongitudinalSpeedMetersPerSecond { get; }
|
|
public double? LongitudinalAccelerationMetersPerSecondSquared { get; }
|
|
public DateTimeOffset CapturedAtUtc { get; }
|
|
public long SequenceId { get; }
|
|
}
|
|
|
|
public sealed class EmPlanningRequest
|
|
{
|
|
public EmPlanningRequest(PathSmoothingResult referencePath, PlanningGridMap map,
|
|
VehicleParameters vehicle, VehicleMotionState vehicleState,
|
|
EmPlannerConfiguration configuration, int segmentIndex,
|
|
EmTrajectory previousTrajectory, DateTimeOffset requestedAtUtc,
|
|
DateTimeOffset effectiveAtUtc, string outputTrajectoryId,
|
|
string referencePathId, string previousTrajectoryId,
|
|
EmMotionModel motionModel);
|
|
}
|
|
|
|
public sealed class DirectionSegmentView
|
|
{
|
|
public int SegmentIndex { get; }
|
|
public TravelDirection Direction { get; }
|
|
public IReadOnlyList<SmoothedPathPoint> Points { get; }
|
|
public ReferenceBoundary StartBoundary { get; }
|
|
public ReferenceBoundary EndBoundary { get; }
|
|
}
|
|
|
|
public sealed class FrenetProjector
|
|
{
|
|
public bool TryProject(Pose2D worldPose, DirectionSegmentView segment,
|
|
double minimumReferenceS, double maximumReferenceS,
|
|
double maximumDistanceMeters, out FrenetProjection projection);
|
|
}
|
|
|
|
public sealed class StaticCorridorBuilder
|
|
{
|
|
public bool TryBuild(DirectionSegmentView segment, double startReferenceS,
|
|
double endReferenceS, IReadOnlyList<FrenetProjection> seed,
|
|
PlanningGridMap map, VehicleParameters vehicle,
|
|
CorridorConfiguration configuration, out StaticCorridor corridor,
|
|
out string failureReason);
|
|
}
|
|
```
|
|
|
|
### Task 1: Verification Host and Immutable Contracts
|
|
|
|
**Files:**
|
|
- Modify: `ClumsyPilot/ClumsyPilot.csproj`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/Program.cs`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/Verification.cs`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/FoundationChecks.cs`
|
|
- Create: all files under `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts/`
|
|
|
|
**Interfaces:**
|
|
- Consumes: existing `Pose2D`, `TravelDirection`, `PathSmoothingResult`, `PlanningGridMap`, and `VehicleParameters`.
|
|
- Produces: the contracts shown in “Shared Public Interfaces”, `EmPlanningStatus`, `EmTerminalType`, `EmBoundaryType`, and immutable trajectory containers.
|
|
|
|
- [ ] **Step 1: Add the isolated verification host and a failing contract check**
|
|
|
|
Add these exact MSBuild rules without rewriting surrounding user changes:
|
|
|
|
```xml
|
|
<Compile Remove="tests\EMPlannerVerificationHost\**\*.cs" />
|
|
<Compile Remove="ParkrobTrajplanner\auto_avoidance\**\*.cs"
|
|
Condition="'$(ExcludeLegacyAutoAvoidance)' == 'true'" />
|
|
```
|
|
|
|
Create the host project with:
|
|
|
|
```xml
|
|
<Project Sdk="Microsoft.NET.Sdk">
|
|
<PropertyGroup>
|
|
<OutputType>Exe</OutputType>
|
|
<TargetFramework>net10.0-windows</TargetFramework>
|
|
<ImplicitUsings>disable</ImplicitUsings>
|
|
<Nullable>enable</Nullable>
|
|
</PropertyGroup>
|
|
<ItemGroup>
|
|
<ProjectReference Include="..\..\ClumsyPilot.csproj"
|
|
AdditionalProperties="ExcludeLegacyAutoAvoidance=true" />
|
|
</ItemGroup>
|
|
</Project>
|
|
```
|
|
|
|
`FoundationChecks.Run()` must construct a reverse `VehicleMotionState`, assert that its signed speed remains negative, construct an `EmTrajectoryPoint`, and assert all constructor-supplied values are unchanged. `Program.Main` accepts `foundation`, runs the check, prints `PASS foundation`, and returns `1` after printing an exception when a check fails.
|
|
|
|
- [ ] **Step 2: Run the host and verify the contracts are absent**
|
|
|
|
Run:
|
|
|
|
```powershell
|
|
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- foundation
|
|
```
|
|
|
|
Expected: build failure naming `VehicleMotionState` or `EmTrajectoryPoint`.
|
|
|
|
- [ ] **Step 3: Implement immutable contracts with constructor validation**
|
|
|
|
Use `ReadOnlyCollection<T>` copies for every published list. `EmTrajectoryPoint` has exactly these public properties:
|
|
|
|
```csharp
|
|
public double X { get; }
|
|
public double Y { get; }
|
|
public double Yaw { get; }
|
|
public double SignedLongitudinalVelocity { get; }
|
|
public double Speed { get; }
|
|
public double VelocityX { get; }
|
|
public double VelocityY { get; }
|
|
public double YawRate { get; }
|
|
public double TimeFromStart { get; }
|
|
public double VehicleCurvature { get; }
|
|
public int SegmentIndex { get; }
|
|
public double SegmentLocalS { get; }
|
|
public double PathS { get; }
|
|
public TravelDirection Direction { get; }
|
|
public EmBoundaryType BoundaryType { get; }
|
|
```
|
|
|
|
Its constructor accepts the authoritative `signedLongitudinalVelocity` and derives, rather than accepts, the redundant fields:
|
|
|
|
```csharp
|
|
Speed = Math.Abs(signedLongitudinalVelocity);
|
|
VelocityX = signedLongitudinalVelocity * Math.Cos(yaw);
|
|
VelocityY = signedLongitudinalVelocity * Math.Sin(yaw);
|
|
YawRate = signedLongitudinalVelocity * vehicleCurvature;
|
|
```
|
|
|
|
Define these exact status values:
|
|
|
|
```text
|
|
Success
|
|
SuccessWithFallback
|
|
InvalidInput
|
|
UnsupportedMotionMode
|
|
StaleVehicleState
|
|
StateDirectionMismatch
|
|
InvalidReferencePath
|
|
ProjectionFailed
|
|
CorridorInfeasible
|
|
LateralInfeasible
|
|
LongitudinalInfeasible
|
|
StoppingDistanceInsufficient
|
|
SolverUnavailable
|
|
SolverTimedOut
|
|
Cancelled
|
|
ValidationFailed
|
|
Superseded
|
|
Failed
|
|
```
|
|
|
|
`EmMotionModel` has one supported value, `NonholonomicForwardReverse`, plus explicit `CrabTranslation` and `InPlaceRotation` values that validation maps to `UnsupportedMotionMode`. Only the two success statuses may construct a result with a non-empty trajectory. Define `EmBoundaryType` as `None`, `RollingSafetyStop`, `GearSwitchApproach`, `GearSwitchDeparture`, and `Goal`; define `EmTerminalType` as `RollingSafetyStop`, `GearSwitch`, and `Goal`.
|
|
|
|
`EmTrajectoryMetadata` contains `TrajectoryId`, `GeneratedAtUtc`, `EffectiveAtUtc`, `MapSnapshotId`, `ReferencePathId`, `VehicleStateSequenceId`, `PreviousTrajectoryId`, `SegmentIndex`, `Direction`, and `TerminalType`; `EmTrajectory` exposes that metadata plus immutable points. `GeneratedAtUtc` is copied from request `RequestedAtUtc`, and `EffectiveAtUtc` plus `OutputTrajectoryId` are caller-supplied so the pure planner never reads a clock or creates a random ID. `EmTrajectoryPoint` retains internal longitudinal acceleration and jerk values for validation but derives all public redundant velocity fields from signed longitudinal velocity.
|
|
|
|
- [ ] **Step 4: Run the foundation check**
|
|
|
|
Run the same command. Expected: `PASS foundation` and exit code `0`.
|
|
|
|
- [ ] **Step 5: Commit the host and contracts**
|
|
|
|
```powershell
|
|
git add ClumsyPilot/ClumsyPilot.csproj ClumsyPilot/tests/EMPlannerVerificationHost ClumsyPilot/ParkrobTrajplanner/EMPlanner/Contracts
|
|
git commit -m "feat: add EM planner contracts"
|
|
```
|
|
|
|
### Task 2: Configuration, Diagnostics, and Request Validation
|
|
|
|
**Files:**
|
|
- Create: all files under `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration/`
|
|
- Create: all files under `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Diagnostics/`
|
|
- Create: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Validation/EmPlanningRequestValidator.cs`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/FoundationChecks.cs`
|
|
|
|
**Interfaces:**
|
|
- Consumes: Task 1 contracts.
|
|
- Produces: `EmPlannerConfiguration.CreateDefault()`, `EmPlanningRequestValidator.Validate(EmPlanningRequest)`, debug options, and diagnostics.
|
|
|
|
- [ ] **Step 1: Write failing checks for exact defaults and rejection rules**
|
|
|
|
Add assertions for these exact defaults:
|
|
|
|
```text
|
|
ReplanPeriodSeconds=0.20
|
|
TimeHorizonSeconds=6.0
|
|
DistanceHorizonMeters=5.0
|
|
OutputTimeStepSeconds=0.05
|
|
SolverTimeoutSeconds=0.10
|
|
HandoffLookaheadSeconds=0.30
|
|
MaximumVehicleStateAgeSeconds=0.20
|
|
LongitudinalSampleSpacingMeters=0.10
|
|
LateralSampleSpacingMeters=0.025
|
|
MaximumLateralOffsetMeters=0.30
|
|
AdditionalClearanceReserveMeters=0.02
|
|
MaximumCollisionCheckStepMeters=0.025
|
|
MaximumProjectionDistanceMeters=0.50
|
|
MinimumFrenetDenominator=0.20
|
|
BoundaryAnchorToleranceMeters=1e-8
|
|
MaximumLateralStepPerIterationMeters=0.05
|
|
MaximumLateralSlope=0.50
|
|
MaximumLateralSecondDerivativePerMeter=1.00
|
|
MaximumLateralThirdDerivativePerSquareMeter=2.00
|
|
MaximumForwardSpeedMetersPerSecond=0.20
|
|
MaximumReverseSpeedMetersPerSecond=0.20
|
|
MaximumAccelerationMetersPerSecondSquared=0.20
|
|
MaximumDecelerationMetersPerSecondSquared=0.30
|
|
MaximumJerkMetersPerSecondCubed=0.50
|
|
MaximumLateralAccelerationMetersPerSecondSquared=0.20
|
|
MaximumCurvatureRatePerMeterPerSecond=0.50
|
|
StopSpeedToleranceMetersPerSecond=0.01
|
|
ZeroSpeedHoldSeconds=0.20
|
|
MaximumOuterIterations=5
|
|
MaximumOsqpIterations=4000
|
|
AbsoluteTolerance=1e-5
|
|
RelativeTolerance=1e-5
|
|
StrictResidualTolerance=1e-5
|
|
WarmStart=true
|
|
Polish=true
|
|
NativeVerbose=false
|
|
```
|
|
|
|
Assert LS weights `10,1,5,10,5,20,5,10` in reference/heading/second/third/curvature/curvature-variation/previous/rolling-terminal order, and ST weights `10,1,10,5,1` in speed/acceleration/jerk/previous/terminal-acceleration order.
|
|
|
|
Also assert validation rejects: null request members, a map with `PlanningReady=false`, a smoothing failure status, an out-of-range segment index, a non-finite speed, negative sequence ID, stale state, and vehicle geometry without a valid curvature limit.
|
|
Assert `CrabTranslation` and `InPlaceRotation` return `UnsupportedMotionMode` before projection.
|
|
|
|
- [ ] **Step 2: Run and verify the configuration types are absent**
|
|
|
|
Run the foundation group. Expected: build failure naming `EmPlannerConfiguration`.
|
|
|
|
- [ ] **Step 3: Implement configuration snapshots and validation**
|
|
|
|
Use settable configuration DTOs only at the request boundary, then make `EmPlanningRequestValidator` return a copied internal snapshot. Validate every double with `NumericGuard`; enforce positive sample spacings, `0 < MinimumFrenetDenominator < 1`, non-negative margins, and positive horizons. Treat only `Complete`, `PartialImprovement`, `NotNeeded`, and `Unchanged` smoothing statuses as consumable.
|
|
|
|
`LateralConfiguration` owns trust-region and derivative hard limits plus `LateralWeights`; `LongitudinalConfiguration` owns direction speed, acceleration, deceleration, jerk, lateral-acceleration, curvature-rate, stop tolerance, hold duration, and `LongitudinalWeights`; `ValidationConfiguration` owns absolute spatial/kinematic consistency tolerances. Configuration validation requires every weight to be finite and non-negative and every normalization scale to be finite and positive.
|
|
|
|
`EmPlannerDebugOptions` exposes only flags and a sink:
|
|
|
|
```csharp
|
|
EnableSummary
|
|
EnableProjectionTrace
|
|
EnableCorridorTrace
|
|
EnableLateralSolverTrace
|
|
EnableLongitudinalSolverTrace
|
|
EnableTrajectoryDump
|
|
EnableVisualization
|
|
Sink
|
|
```
|
|
|
|
Debug-sink exceptions are caught and recorded in diagnostics; they never escape the planner.
|
|
|
|
- [ ] **Step 4: Run foundation checks and confirm all rejection messages are deterministic**
|
|
|
|
Expected: `PASS foundation`; run twice and compare stdout exactly.
|
|
|
|
- [ ] **Step 5: Commit configuration and validation**
|
|
|
|
```powershell
|
|
git add ClumsyPilot/ParkrobTrajplanner/EMPlanner/Configuration ClumsyPilot/ParkrobTrajplanner/EMPlanner/Diagnostics ClumsyPilot/ParkrobTrajplanner/EMPlanner/Validation ClumsyPilot/tests/EMPlannerVerificationHost/FoundationChecks.cs
|
|
git commit -m "feat: validate EM planner requests"
|
|
```
|
|
|
|
### Task 3: Direction Segmentation and Exact Boundary Anchors
|
|
|
|
**Files:**
|
|
- Create: all files under `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Segmentation/`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/SegmentationChecks.cs`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/Program.cs`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/EmFixtureFactory.cs`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `PathSmoothingResult.Path`, `PathSmoothingResult.Segments`, and Task 1 boundary enums.
|
|
- Produces: `DirectionSegmentView`, `ReferenceBoundary`, and exact horizon slices.
|
|
|
|
- [ ] **Step 1: Write failing gear-pair and horizon checks**
|
|
|
|
Build a fixture whose forward segment ends at `(2,0,0,s=2)`, whose reverse segment starts with a duplicated pose at the same source arc length, and whose direction changes only on the second member of the pair. Assert:
|
|
|
|
```text
|
|
segment 0 end boundary = GearSwitchApproach
|
|
segment 1 start boundary = GearSwitchDeparture
|
|
segment identity differs even when pose and source arc length match
|
|
horizon 1.95 injects an interpolated RollingSafetyStop at exactly 1.95
|
|
horizon 2.05 for segment 0 still ends at exactly 2.00 and never includes segment 1
|
|
```
|
|
|
|
- [ ] **Step 2: Run the segmentation group and verify failure**
|
|
|
|
```powershell
|
|
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- segmentation
|
|
```
|
|
|
|
Expected: build failure naming `ReferencePathSegmenter`.
|
|
|
|
- [ ] **Step 3: Implement segment views and exact slicing**
|
|
|
|
`ReferenceBoundary` identity is the tuple `(SegmentIndex, SegmentLocalS, BoundaryType)`. Rebase every selected segment to `SegmentLocalS=0` without changing the source point. `ReferenceHorizonSlicer.Slice` must interpolate X, Y, unwrapped vehicle yaw, geometric curvature, vehicle curvature, curvature derivative, and clearance at the exact terminal S, then label the anchor `RollingSafetyStop`, `GearSwitch`, or `Goal`.
|
|
|
|
Never implement horizon selection by filtering `point.ArcLength <= end`; always append or replace with the exact anchor after bracketing interpolation.
|
|
|
|
- [ ] **Step 4: Run segmentation checks**
|
|
|
|
Expected: `PASS segmentation`, with assertions covering exact equality within `1e-8 m`.
|
|
|
|
- [ ] **Step 5: Commit segmentation**
|
|
|
|
```powershell
|
|
git add ClumsyPilot/ParkrobTrajplanner/EMPlanner/Segmentation ClumsyPilot/tests/EMPlannerVerificationHost
|
|
git commit -m "feat: preserve EM planner segment boundaries"
|
|
```
|
|
|
|
### Task 4: Reverse-Safe Frenet Projection and Reconstruction
|
|
|
|
**Files:**
|
|
- Create: all files under `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Frenet/`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/FrenetChecks.cs`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/Program.cs`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `DirectionSegmentView` from Task 3.
|
|
- Produces: interpolation, bounded projection, world/Frenet conversion, and optimized-yaw reconstruction.
|
|
|
|
- [ ] **Step 1: Write failing forward, reverse, wraparound, and loop checks**
|
|
|
|
Cover these equations directly:
|
|
|
|
```text
|
|
travelYaw = forward ? vehicleYaw : Normalize(vehicleYaw + PI)
|
|
deltaS = dx*cos(travelYaw) + dy*sin(travelYaw)
|
|
l = -dx*sin(travelYaw) + dy*cos(travelYaw)
|
|
x = referenceX - l*sin(travelYaw)
|
|
y = referenceY + l*cos(travelYaw)
|
|
optimizedTravelYaw = referenceTravelYaw + atan2(dl, 1-referenceK*l)
|
|
optimizedVehicleYaw = reverse ? Normalize(optimizedTravelYaw + PI) : optimizedTravelYaw
|
|
```
|
|
|
|
Use a reverse reference whose vehicle yaw is near `-π`, points on both sides of the wrap, and a U-shaped segment with two spatially close branches. Assert the projector remains inside the supplied S interval and chooses the locally nearest branch, then reconstructs the original world point within `1e-8 m`.
|
|
|
|
- [ ] **Step 2: Run the Frenet group and verify failure**
|
|
|
|
Expected: build failure naming `FrenetProjector`.
|
|
|
|
- [ ] **Step 3: Implement deterministic bounded projection**
|
|
|
|
Use segment-line projection for the coarse candidate, clamp the interpolation fraction to `[0,1]`, then compare squared world distance. Resolve equal-distance ties by smaller absolute delta from the seed/reference S and then smaller S. Reject projections farther than `MaximumProjectionDistanceMeters`; never search another direction segment.
|
|
|
|
`FrenetTransform` rejects reconstruction when `1-referenceK*l < MinimumFrenetDenominator`. Angles used for interpolation are unwrapped; only public yaw is normalized with `AngleMath.NormalizeRadians`.
|
|
|
|
- [ ] **Step 4: Run Frenet checks**
|
|
|
|
Expected: `PASS frenet`, including reverse `l>0` being left of motion and therefore body-right.
|
|
|
|
- [ ] **Step 5: Commit Frenet support**
|
|
|
|
```powershell
|
|
git add ClumsyPilot/ParkrobTrajplanner/EMPlanner/Frenet ClumsyPilot/tests/EMPlannerVerificationHost
|
|
git commit -m "feat: add reverse-safe Frenet transforms"
|
|
```
|
|
|
|
### Task 5: Topology-Preserving Static Corridor
|
|
|
|
**Files:**
|
|
- Create: all files under `ClumsyPilot/ParkrobTrajplanner/EMPlanner/Corridor/`
|
|
- Create: `ClumsyPilot/tests/EMPlannerVerificationHost/CorridorChecks.cs`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/Program.cs`
|
|
|
|
**Interfaces:**
|
|
- Consumes: Task 4 interpolation/transforms, `PlanningGridMap`, `VehicleParameters`, and `FootprintCollisionChecker`.
|
|
- Produces: immutable `StaticCorridor` stations containing `ReferenceS`, `MinimumL`, `MaximumL`, and seed L.
|
|
|
|
- [ ] **Step 1: Write failing corridor connectivity checks**
|
|
|
|
Create maps through `PlanningMapFactory`: an empty map, a static rectangle narrowing the left side of a straight path, and an obstacle splitting lateral samples into disconnected left/right intervals. Assert:
|
|
|
|
```text
|
|
empty map corridor = [-0.30, +0.30] at interior stations
|
|
every accepted lateral sample passes exact rotated-footprint collision checking
|
|
prior-trajectory seed is used before l=0
|
|
the chosen interval contains the seed at every station
|
|
a disappearing seed-connected interval returns false instead of switching sides
|
|
first and last stations match exact requested ReferenceS anchors
|
|
```
|
|
|
|
- [ ] **Step 2: Run the corridor group and verify failure**
|
|
|
|
Expected: build failure naming `StaticCorridorBuilder`.
|
|
|
|
- [ ] **Step 3: Implement broad-phase plus exact-footprint sampling**
|
|
|
|
At each `0.10 m` S station, sample L in `0.025 m` increments from `-0.30` to `+0.30`, always injecting the exact seed L and exact offset limits. Use the distance field only to accept obviously clear samples; all remaining samples go through `FootprintCollisionChecker.IsPoseCollisionFree` with `AdditionalClearanceReserveMeters=0.02`. Group adjacent free samples, choose only the group containing the seed, and propagate overlap with the previously chosen interval.
|
|
|
|
When propagation loses overlap, return failure with the first failed S. Do not select another free group, invert L for reverse, or infer a new obstacle side.
|
|
|
|
- [ ] **Step 4: Run all foundation-plan checks**
|
|
|
|
```powershell
|
|
dotnet run --project ClumsyPilot/tests/EMPlannerVerificationHost/EMPlannerVerificationHost.csproj -- all-foundation
|
|
```
|
|
|
|
Expected output:
|
|
|
|
```text
|
|
PASS foundation
|
|
PASS segmentation
|
|
PASS frenet
|
|
PASS corridor
|
|
```
|
|
|
|
- [ ] **Step 5: Commit corridor support**
|
|
|
|
```powershell
|
|
git add ClumsyPilot/ParkrobTrajplanner/EMPlanner/Corridor ClumsyPilot/tests/EMPlannerVerificationHost
|
|
git commit -m "feat: build static EM lateral corridors"
|
|
```
|
|
|
|
### Task 6: Foundation Documentation and Gate
|
|
|
|
**Files:**
|
|
- Create: `ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md`
|
|
- Modify: `ClumsyPilot/tests/EMPlannerVerificationHost/Program.cs`
|
|
|
|
**Interfaces:**
|
|
- Consumes: all preceding tasks.
|
|
- Produces: a documented, repeatable foundation verification command for later plans.
|
|
|
|
- [ ] **Step 1: Add README contract examples**
|
|
|
|
Document the pipeline, coordinate formulas, reverse sign example, exact gear-pair behavior, configuration units, and the command used to run `all-foundation`. State explicitly that the module does not yet solve LS or ST at this gate.
|
|
|
|
- [ ] **Step 2: Run format and placeholder checks**
|
|
|
|
```powershell
|
|
git diff --check
|
|
rg -n "NotImplementedException|throw new Exception\(\)" ClumsyPilot/ParkrobTrajplanner/EMPlanner ClumsyPilot/tests/EMPlannerVerificationHost
|
|
```
|
|
|
|
Expected: `git diff --check` returns no diagnostics and the incomplete-implementation scan returns no matches.
|
|
|
|
- [ ] **Step 3: Run the complete foundation gate twice**
|
|
|
|
Run `all-foundation` twice. Expected: identical four PASS lines and exit code `0` both times.
|
|
|
|
- [ ] **Step 4: Confirm the normal project baseline separately**
|
|
|
|
```powershell
|
|
dotnet build ClumsyPilot/ClumsyPilot.csproj --no-restore
|
|
```
|
|
|
|
Expected at this repository baseline: only the already-documented legacy `auto_avoidance/MultiWheelAutoAvoidance.cs` missing-reference errors may remain. Any new error under `EMPlanner` fails this gate.
|
|
|
|
- [ ] **Step 5: Commit documentation**
|
|
|
|
```powershell
|
|
git add ClumsyPilot/ParkrobTrajplanner/EMPlanner/README.md ClumsyPilot/tests/EMPlannerVerificationHost/Program.cs
|
|
git commit -m "docs: describe EM planner foundation"
|
|
```
|
|
|
|
## Completion Gate
|
|
|
|
- `all-foundation` passes twice with deterministic output.
|
|
- Forward and reverse projection/reconstruction agree within `1e-8 m`.
|
|
- No segment, projection, horizon, or corridor crosses a gear boundary.
|
|
- Corridor samples pass exact body collision checks and never change disconnected topology.
|
|
- No solver, scheduler, hardware access, dynamic prediction, or trajectory publication logic has leaked into this phase.
|