feat: publish rolling trajectories without stop tails
This commit is contained in:
@@ -15,7 +15,8 @@ public sealed class EmTrajectoryMetadata
|
||||
string previousTrajectoryId,
|
||||
int segmentIndex,
|
||||
TravelDirection direction,
|
||||
EmTerminalType terminalType)
|
||||
EmTerminalType terminalType,
|
||||
EmLongitudinalMode longitudinalMode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(trajectoryId))
|
||||
throw new ArgumentException("A trajectory ID is required.", nameof(trajectoryId));
|
||||
@@ -31,6 +32,8 @@ public sealed class EmTrajectoryMetadata
|
||||
throw new ArgumentOutOfRangeException(nameof(direction));
|
||||
if (!Enum.IsDefined(typeof(EmTerminalType), terminalType))
|
||||
throw new ArgumentOutOfRangeException(nameof(terminalType));
|
||||
if (!Enum.IsDefined(typeof(EmLongitudinalMode), longitudinalMode))
|
||||
throw new ArgumentOutOfRangeException(nameof(longitudinalMode));
|
||||
|
||||
TrajectoryId = trajectoryId;
|
||||
GeneratedAtUtc = generatedAtUtc;
|
||||
@@ -42,6 +45,7 @@ public sealed class EmTrajectoryMetadata
|
||||
SegmentIndex = segmentIndex;
|
||||
Direction = direction;
|
||||
TerminalType = terminalType;
|
||||
LongitudinalMode = longitudinalMode;
|
||||
}
|
||||
|
||||
public string TrajectoryId { get; }
|
||||
@@ -54,4 +58,5 @@ public sealed class EmTrajectoryMetadata
|
||||
public int SegmentIndex { get; }
|
||||
public TravelDirection Direction { get; }
|
||||
public EmTerminalType TerminalType { get; }
|
||||
public EmLongitudinalMode LongitudinalMode { get; }
|
||||
}
|
||||
|
||||
@@ -97,12 +97,12 @@ public sealed class EmPlanningService : IEmPlanningService
|
||||
|
||||
var metadata = new EmTrajectoryMetadata(request.OutputTrajectoryId, request.RequestedAtUtc, request.EffectiveAtUtc,
|
||||
request.Map.SnapshotId, request.ReferencePathId, request.VehicleState.SequenceId, request.PreviousTrajectoryId,
|
||||
segment.SegmentIndex, segment.Direction, horizon.TerminalType);
|
||||
segment.SegmentIndex, segment.Direction, horizon.TerminalType, horizon.LongitudinalMode);
|
||||
EmTrajectory trajectory = new EmTrajectoryAssembler(configuration).Assemble(lateral.Path, longitudinal, metadata);
|
||||
EmitDebug(request, "trajectory assembly succeeded");
|
||||
|
||||
EmTrajectoryValidationResult publication = new EmTrajectoryValidator().Validate(trajectory, request.Map,
|
||||
request.Vehicle, configuration, segment.SegmentIndex, longitudinalInput.TerminalPathS,
|
||||
request.Vehicle, configuration, segment.SegmentIndex, longitudinalInput.PathUpperBoundS,
|
||||
slice.TerminalBoundary.BoundaryType);
|
||||
if (!publication.IsValid)
|
||||
{
|
||||
|
||||
@@ -39,7 +39,8 @@ public sealed class EmTrajectoryAssembler
|
||||
throw new ArgumentNullException(nameof(metadata));
|
||||
|
||||
var interpolator = new LateralPathInterpolator(path);
|
||||
var schedule = new TrajectorySampleSchedule(longitudinal.Candidate, outputTimeStepSeconds, zeroSpeedHoldSeconds);
|
||||
var schedule = new TrajectorySampleSchedule(longitudinal.Candidate, outputTimeStepSeconds, zeroSpeedHoldSeconds,
|
||||
metadata.LongitudinalMode);
|
||||
double terminalPathS = path.Points[path.Points.Count - 1].PathS;
|
||||
var points = new List<EmTrajectoryPoint>(schedule.Samples.Count);
|
||||
double directionSign = metadata.Direction == TravelDirection.Forward ? 1d : -1d;
|
||||
@@ -51,7 +52,8 @@ public sealed class EmTrajectoryAssembler
|
||||
throw new ArgumentException("Longitudinal PathS exceeds the assembled lateral path.", nameof(longitudinal));
|
||||
|
||||
InterpolatedLateralPathPoint geometry = interpolator.Interpolate(sample.PathS);
|
||||
bool isTerminalAnchor = index == longitudinal.Candidate.KnotTimes.Count - 1;
|
||||
bool isTerminalAnchor = metadata.LongitudinalMode == EmLongitudinalMode.ExactStopAtBoundary &&
|
||||
index == schedule.TerminalAnchorSampleIndex;
|
||||
EmBoundaryType boundaryType = isTerminalAnchor ? ToBoundaryType(metadata.TerminalType) : EmBoundaryType.None;
|
||||
double signedSpeed = directionSign * sample.ProgressSpeed;
|
||||
points.Add(new EmTrajectoryPoint(geometry.X, geometry.Y, geometry.Yaw, signedSpeed, sample.TimeFromStart,
|
||||
|
||||
@@ -8,7 +8,8 @@ internal sealed class TrajectorySampleSchedule
|
||||
{
|
||||
private const double ZeroTolerance = 1e-12d;
|
||||
|
||||
public TrajectorySampleSchedule(LongitudinalCandidate candidate, double outputTimeStepSeconds, double holdDurationSeconds)
|
||||
public TrajectorySampleSchedule(LongitudinalCandidate candidate, double outputTimeStepSeconds, double holdDurationSeconds,
|
||||
EmLongitudinalMode mode)
|
||||
{
|
||||
if (candidate == null)
|
||||
throw new ArgumentNullException(nameof(candidate));
|
||||
@@ -16,8 +17,8 @@ internal sealed class TrajectorySampleSchedule
|
||||
throw new ArgumentOutOfRangeException(nameof(outputTimeStepSeconds));
|
||||
if (!IsFinite(holdDurationSeconds) || holdDurationSeconds < 0d)
|
||||
throw new ArgumentOutOfRangeException(nameof(holdDurationSeconds));
|
||||
if (Math.Abs(candidate.U[candidate.U.Count - 1]) > ZeroTolerance)
|
||||
throw new ArgumentException("A publishable trajectory requires an exact zero-speed terminal candidate.", nameof(candidate));
|
||||
if (!Enum.IsDefined(typeof(EmLongitudinalMode), mode))
|
||||
throw new ArgumentOutOfRangeException(nameof(mode));
|
||||
|
||||
var samples = new List<TrajectorySample>(candidate.KnotTimes.Count + 4);
|
||||
double previousPathS = double.NegativeInfinity;
|
||||
@@ -33,6 +34,27 @@ internal sealed class TrajectorySampleSchedule
|
||||
previousPathS = candidate.S[index];
|
||||
}
|
||||
|
||||
if (mode != EmLongitudinalMode.ExactStopAtBoundary)
|
||||
{
|
||||
Samples = new ReadOnlyCollection<TrajectorySample>(samples);
|
||||
TerminalAnchorSampleIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
int stabilizationStart = LongitudinalTerminalSchedule.GetStabilizationStartIndex(candidate.KnotTimes,
|
||||
outputTimeStepSeconds);
|
||||
double stopPathS = candidate.S[stabilizationStart];
|
||||
for (int index = stabilizationStart; index < candidate.S.Count; index++)
|
||||
{
|
||||
if (Math.Abs(candidate.S[index] - stopPathS) > ZeroTolerance ||
|
||||
Math.Abs(candidate.U[index]) > ZeroTolerance || Math.Abs(candidate.A[index]) > ZeroTolerance ||
|
||||
(index < candidate.J.Count && Math.Abs(candidate.J[index]) > ZeroTolerance))
|
||||
{
|
||||
throw new ArgumentException("An exact stop requires a stationary S/U/A/J tail.", nameof(candidate));
|
||||
}
|
||||
}
|
||||
|
||||
TerminalAnchorSampleIndex = stabilizationStart;
|
||||
TrajectorySample terminal = samples[samples.Count - 1];
|
||||
double holdElapsed = 0d;
|
||||
while (holdElapsed < holdDurationSeconds - ZeroTolerance)
|
||||
@@ -45,6 +67,7 @@ internal sealed class TrajectorySampleSchedule
|
||||
}
|
||||
|
||||
public IReadOnlyList<TrajectorySample> Samples { get; }
|
||||
public int TerminalAnchorSampleIndex { get; }
|
||||
|
||||
private static bool IsFinite(double value)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ public enum EmTrajectoryValidationFailure
|
||||
SegmentBoundaryExceeded,
|
||||
MissingTerminalAnchor,
|
||||
TerminalSpeedNotZero,
|
||||
TerminalAccelerationNotZero,
|
||||
TerminalYawRateNotZero,
|
||||
DirectionMismatch,
|
||||
DirectionSignMismatch,
|
||||
@@ -77,11 +78,11 @@ public sealed class EmTrajectoryValidator
|
||||
}
|
||||
|
||||
public EmTrajectoryValidationResult Validate(EmTrajectory trajectory, PlanningGridMap map, VehicleParameters vehicle,
|
||||
EmPlannerConfiguration configuration, int segmentIndex, double terminalPathS, EmBoundaryType terminalBoundary)
|
||||
EmPlannerConfiguration configuration, int segmentIndex, double pathUpperBoundS, EmBoundaryType terminalBoundary)
|
||||
{
|
||||
if (trajectory == null || map == null || vehicle == null || configuration == null || configuration.Validation == null ||
|
||||
configuration.Longitudinal == null || configuration.Corridor == null || segmentIndex < 0 ||
|
||||
!IsFinite(terminalPathS) || terminalPathS < 0d || !Enum.IsDefined(typeof(EmBoundaryType), terminalBoundary) ||
|
||||
!IsFinite(pathUpperBoundS) || pathUpperBoundS < 0d || !Enum.IsDefined(typeof(EmBoundaryType), terminalBoundary) ||
|
||||
!TryReadLimits(configuration, vehicle, trajectory.Metadata.Direction, out ValidationLimits limits))
|
||||
{
|
||||
return EmTrajectoryValidationResult.Reject(EmTrajectoryValidationFailure.InvalidInput, -1,
|
||||
@@ -93,8 +94,8 @@ public sealed class EmTrajectoryValidator
|
||||
EmTrajectoryPoint point = trajectory.Points[index];
|
||||
if (point == null || !HasOnlyFiniteValues(point))
|
||||
return Reject(EmTrajectoryValidationFailure.NonFinite, index, "Trajectory contains a non-finite point.");
|
||||
if (point.SegmentIndex != segmentIndex || point.SegmentLocalS > terminalPathS + limits.SpatialTolerance ||
|
||||
point.PathS > terminalPathS + limits.SpatialTolerance)
|
||||
if (point.SegmentIndex != segmentIndex || point.SegmentLocalS > pathUpperBoundS + limits.SpatialTolerance ||
|
||||
point.PathS > pathUpperBoundS + limits.SpatialTolerance)
|
||||
{
|
||||
return Reject(EmTrajectoryValidationFailure.SegmentBoundaryExceeded, index,
|
||||
"Trajectory point lies outside the current direction segment.");
|
||||
@@ -114,21 +115,43 @@ public sealed class EmTrajectoryValidator
|
||||
}
|
||||
}
|
||||
|
||||
int terminalIndex = FindTerminalAnchor(trajectory, terminalPathS, terminalBoundary, limits.SpatialTolerance);
|
||||
if (terminalIndex < 0)
|
||||
int terminalIndex = -1;
|
||||
if (trajectory.Metadata.LongitudinalMode == EmLongitudinalMode.ExactStopAtBoundary)
|
||||
{
|
||||
return Reject(EmTrajectoryValidationFailure.MissingTerminalAnchor,
|
||||
FindFirstTerminalPathIndex(trajectory, terminalPathS, limits.SpatialTolerance),
|
||||
"Trajectory does not contain the exact terminal boundary anchor.");
|
||||
}
|
||||
terminalIndex = FindTerminalAnchor(trajectory, pathUpperBoundS, terminalBoundary, limits.SpatialTolerance);
|
||||
if (terminalIndex < 0)
|
||||
{
|
||||
return Reject(EmTrajectoryValidationFailure.MissingTerminalAnchor,
|
||||
FindFirstTerminalPathIndex(trajectory, pathUpperBoundS, limits.SpatialTolerance),
|
||||
"Trajectory does not contain the exact terminal boundary anchor.");
|
||||
}
|
||||
|
||||
EmTrajectoryPoint terminal = trajectory.Points[terminalIndex];
|
||||
if (Math.Abs(terminal.SignedLongitudinalVelocity) > limits.KinematicTolerance)
|
||||
return Reject(EmTrajectoryValidationFailure.TerminalSpeedNotZero, terminalIndex,
|
||||
"Terminal signed speed must be zero.");
|
||||
if (Math.Abs(terminal.YawRate) > limits.KinematicTolerance)
|
||||
return Reject(EmTrajectoryValidationFailure.TerminalYawRateNotZero, terminalIndex,
|
||||
"Terminal yaw rate must be zero.");
|
||||
EmTrajectoryPoint terminal = trajectory.Points[terminalIndex];
|
||||
if (Math.Abs(terminal.SignedLongitudinalVelocity) > limits.KinematicTolerance)
|
||||
return Reject(EmTrajectoryValidationFailure.TerminalSpeedNotZero, terminalIndex,
|
||||
"Terminal signed speed must be zero.");
|
||||
if (Math.Abs(terminal.LongitudinalAcceleration) > limits.KinematicTolerance)
|
||||
return Reject(EmTrajectoryValidationFailure.TerminalAccelerationNotZero, terminalIndex,
|
||||
"Terminal stored acceleration must be zero.");
|
||||
if (Math.Abs(terminal.YawRate) > limits.KinematicTolerance)
|
||||
return Reject(EmTrajectoryValidationFailure.TerminalYawRateNotZero, terminalIndex,
|
||||
"Terminal yaw rate must be zero.");
|
||||
for (int index = terminalIndex; index < trajectory.Points.Count; index++)
|
||||
{
|
||||
EmTrajectoryPoint tail = trajectory.Points[index];
|
||||
if (Math.Abs(tail.PathS - terminal.PathS) > limits.SpatialTolerance)
|
||||
{
|
||||
return Reject(EmTrajectoryValidationFailure.MissingTerminalAnchor, index,
|
||||
"Exact-stop tail must remain at the terminal PathS.");
|
||||
}
|
||||
if (Math.Abs(tail.SignedLongitudinalVelocity) > limits.KinematicTolerance)
|
||||
return Reject(EmTrajectoryValidationFailure.TerminalSpeedNotZero, index,
|
||||
"Exact-stop tail signed speed must remain zero.");
|
||||
if (Math.Abs(tail.LongitudinalAcceleration) > limits.KinematicTolerance)
|
||||
return Reject(EmTrajectoryValidationFailure.TerminalAccelerationNotZero, index,
|
||||
"Exact-stop tail stored acceleration must remain zero.");
|
||||
}
|
||||
}
|
||||
|
||||
double directionSign = trajectory.Metadata.Direction == TravelDirection.Forward ? 1d : -1d;
|
||||
double previousAcceleration = 0d;
|
||||
|
||||
Reference in New Issue
Block a user