test: cover rolling-to-stop EM planning flow

This commit is contained in:
梁薄云
2026-08-05 21:17:53 +08:00
parent 2eb7902bc3
commit eb050b19e4
3 changed files with 111 additions and 10 deletions
@@ -15,6 +15,7 @@ internal static class EmPlanningServiceChecks
{
VerifiesPreviousTrajectoryIsALongitudinalSoftReference();
VerifiesForwardReverseAndBoundarySuccessesAreDeterministic();
VerifiesServicePublishesRollingApproachAndExactStopModes();
VerifiesRequestAndStateFailuresPublishNoTrajectory();
VerifiesProjectionCorridorAndOptimizationFailuresPublishNoTrajectory();
VerifiesTimeoutFallbackAndCancellationSemantics();
@@ -50,6 +51,100 @@ internal static class EmPlanningServiceChecks
VerifySuccess(rolling, rollingRequest, EmTerminalType.RollingSafetyStop, "rolling stop");
}
private static void VerifiesServicePublishesRollingApproachAndExactStopModes()
{
EmPlanningRequest rollingRequest = CreateRequest(TravelDirection.Forward, 0.10d, false, false,
CreateReferencePath(TravelDirection.Forward, false, 10d), CreateMap(false, 12d));
ConfigureFiveMeterWindowAndTwoSecondHorizon(rollingRequest.Configuration);
EmPlanningResult rolling = new EmPlanningService(new ScriptedPipelineSolver(PipelineSolverMode.Success)).Plan(
rollingRequest, CancellationToken.None);
VerifySuccess(rolling, rollingRequest, EmTerminalType.RollingSafetyStop, "cycle 1 rolling");
Verification.Equal(EmLongitudinalMode.RollingContinuation,
rolling.Trajectory.Metadata.LongitudinalMode, "cycle 1 rolls");
Verification.Equal(21, rolling.Trajectory.Points.Count, "rolling publishes the two-second ST knot count");
EmTrajectoryPoint rollingTerminal = rolling.Trajectory.Points[rolling.Trajectory.Points.Count - 1];
Verification.True(rollingTerminal.PathS < 5d, "two-second ST output remains inside the five-metre LS window");
Verification.True(rollingTerminal.SignedLongitudinalVelocity != 0d, "cycle 1 has nonzero terminal speed");
EmPlanningRequest approachRequest = CreateRequest(TravelDirection.Forward, 0.10d, false, false,
CreateReferencePath(TravelDirection.Forward, false, 4d), CreateMap(false, 5d));
EmPlanningResult approach = new EmPlanningService(new ScriptedPipelineSolver(PipelineSolverMode.Success)).Plan(
approachRequest, CancellationToken.None);
VerifySuccess(approach, approachRequest, EmTerminalType.Goal, "cycle 2 approach");
Verification.Equal(EmLongitudinalMode.ApproachStopBoundary,
approach.Trajectory.Metadata.LongitudinalMode, "cycle 2 approaches");
Verification.True(approach.Trajectory.Points[approach.Trajectory.Points.Count - 1].SignedLongitudinalVelocity != 0d,
"approach has no synthetic stop tail");
EmPlanningRequest exactRequest = CreateRequest(TravelDirection.Forward, 0.05d, false, false,
CreateReferencePath(TravelDirection.Forward, false, 0.0075d));
ConfigureExactStopServiceScenario(exactRequest.Configuration);
EmPlanningResult exact = new EmPlanningService(new ScriptedPipelineSolver(PipelineSolverMode.Success)).Plan(
exactRequest, CancellationToken.None);
VerifySuccess(exact, exactRequest, EmTerminalType.Goal, "cycle 3 exact goal stop");
Verification.Equal(EmLongitudinalMode.ExactStopAtBoundary,
exact.Trajectory.Metadata.LongitudinalMode, "cycle 3 stops");
AssertExactStopStabilization(exact.Trajectory, EmBoundaryType.Goal, "goal");
EmPlanningRequest gearRequest = CreateRequest(TravelDirection.Forward, 0.05d, false, false,
EmFixtureFactory.CreateGearPairReferencePath(0.0075d));
ConfigureExactStopServiceScenario(gearRequest.Configuration);
EmPlanningResult gear = new EmPlanningService(new ScriptedPipelineSolver(PipelineSolverMode.Success)).Plan(
gearRequest, CancellationToken.None);
VerifySuccess(gear, gearRequest, EmTerminalType.GearSwitch, "gear-switch exact stop");
Verification.Equal(EmLongitudinalMode.ExactStopAtBoundary,
gear.Trajectory.Metadata.LongitudinalMode, "gear switch stops exactly");
AssertExactStopStabilization(gear.Trajectory, EmBoundaryType.GearSwitchApproach, "gear switch");
for (int index = 0; index < gear.Trajectory.Points.Count; index++)
{
Verification.Equal(TravelDirection.Forward, gear.Trajectory.Points[index].Direction,
"gear-switch publication excludes the next direction point " + index);
}
}
private static void ConfigureFiveMeterWindowAndTwoSecondHorizon(EmPlannerConfiguration configuration)
{
configuration.Scheduling.DistanceHorizonMeters = 5d;
configuration.Scheduling.TimeHorizonSeconds = 2d;
configuration.Scheduling.OutputTimeStepSeconds = 0.1d;
configuration.Longitudinal.MaximumForwardSpeedMetersPerSecond = 0.2d;
}
private static void ConfigureExactStopServiceScenario(EmPlannerConfiguration configuration)
{
configuration.Scheduling.TimeHorizonSeconds = 0.40d;
configuration.Scheduling.OutputTimeStepSeconds = 0.10d;
configuration.Longitudinal.MaximumForwardSpeedMetersPerSecond = 1d;
configuration.Longitudinal.MaximumReverseSpeedMetersPerSecond = 1d;
configuration.Longitudinal.MaximumAccelerationMetersPerSecondSquared = 1e-6d;
configuration.Longitudinal.MaximumDecelerationMetersPerSecondSquared = 1d;
configuration.Longitudinal.MaximumJerkMetersPerSecondCubed = 20d;
configuration.Longitudinal.MaximumLateralAccelerationMetersPerSecondSquared = 1d;
configuration.Longitudinal.MaximumCurvatureRatePerMeterPerSecond = 1d;
}
private static void AssertExactStopStabilization(EmTrajectory trajectory, EmBoundaryType boundaryType, string name)
{
int exactAnchor = -1;
for (int index = 0; index < trajectory.Points.Count; index++)
{
if (trajectory.Points[index].BoundaryType == boundaryType)
{
exactAnchor = index;
break;
}
}
Verification.True(exactAnchor >= 0, name + " has a real boundary anchor");
Verification.NearlyEqual(0d, trajectory.Points[exactAnchor].SignedLongitudinalVelocity,
name + " speed is zero");
Verification.True(trajectory.Points.Count > exactAnchor + 1,
name + " anchor is followed by a QP stabilization point");
Verification.NearlyEqual(trajectory.Points[exactAnchor].PathS, trajectory.Points[exactAnchor + 1].PathS,
name + " stabilization keeps the stop position");
Verification.NearlyEqual(0d, trajectory.Points[exactAnchor + 1].SignedLongitudinalVelocity,
name + " stabilization speed is zero");
}
private static void VerifiesRequestAndStateFailuresPublishNoTrajectory()
{
EmPlanningRequest invalidSmoothing = CreateRequest(TravelDirection.Forward, 0d, false, false,
@@ -300,7 +395,7 @@ internal static class EmPlanningServiceChecks
new PathSmoothingDiagnostics(metrics, TimeSpan.Zero), new List<PathSmoothingRegionReport>());
}
private static PlanningGridMap CreateMap(bool blockStart)
private static PlanningGridMap CreateMap(bool blockStart, double halfExtentMeters = 3d)
{
IMapObstacleSource[] sources = blockStart
? new IMapObstacleSource[] { new ManualObstacleSource("service-obstacle", 1L, true,
@@ -308,7 +403,7 @@ internal static class EmPlanningServiceChecks
: Array.Empty<IMapObstacleSource>();
PlanningMapBuildResult result = new PlanningMapFactory().Create(new PlanningMapRequest
{
Bounds = new MapBoundsMm(-3000f, 3000f, -1000f, 1000f),
Bounds = new MapBoundsMm((float)(-1000d * halfExtentMeters), (float)(1000d * halfExtentMeters), -1000f, 1000f),
ResolutionMm = 20f,
ObstacleSources = sources,
AllowExplicitEmptyMap = !blockStart,