fix: normalize EM PathS resampling residuals
This commit is contained in:
@@ -9,6 +9,7 @@ public sealed class EmTrajectoryAssembler
|
|||||||
{
|
{
|
||||||
private readonly double outputTimeStepSeconds;
|
private readonly double outputTimeStepSeconds;
|
||||||
private readonly double zeroSpeedHoldSeconds;
|
private readonly double zeroSpeedHoldSeconds;
|
||||||
|
private readonly double pathSTolerance;
|
||||||
private readonly int maximumPublishedSampleCount;
|
private readonly int maximumPublishedSampleCount;
|
||||||
|
|
||||||
public EmTrajectoryAssembler()
|
public EmTrajectoryAssembler()
|
||||||
@@ -18,13 +19,16 @@ public sealed class EmTrajectoryAssembler
|
|||||||
|
|
||||||
public EmTrajectoryAssembler(EmPlannerConfiguration configuration)
|
public EmTrajectoryAssembler(EmPlannerConfiguration configuration)
|
||||||
{
|
{
|
||||||
if (configuration == null || configuration.Scheduling == null || configuration.Longitudinal == null)
|
if (configuration == null || configuration.Scheduling == null || configuration.Longitudinal == null ||
|
||||||
|
configuration.Validation == null)
|
||||||
throw new ArgumentNullException(nameof(configuration));
|
throw new ArgumentNullException(nameof(configuration));
|
||||||
outputTimeStepSeconds = configuration.Scheduling.OutputTimeStepSeconds;
|
outputTimeStepSeconds = configuration.Scheduling.OutputTimeStepSeconds;
|
||||||
zeroSpeedHoldSeconds = configuration.Longitudinal.ZeroSpeedHoldSeconds;
|
zeroSpeedHoldSeconds = configuration.Longitudinal.ZeroSpeedHoldSeconds;
|
||||||
|
pathSTolerance = configuration.Validation.KinematicTolerance;
|
||||||
maximumPublishedSampleCount = configuration.Scheduling.MaximumPublishedSampleCount;
|
maximumPublishedSampleCount = configuration.Scheduling.MaximumPublishedSampleCount;
|
||||||
if (!IsFinite(outputTimeStepSeconds) || outputTimeStepSeconds <= 0d || !IsFinite(zeroSpeedHoldSeconds) ||
|
if (!IsFinite(outputTimeStepSeconds) || outputTimeStepSeconds <= 0d || !IsFinite(zeroSpeedHoldSeconds) ||
|
||||||
zeroSpeedHoldSeconds < 0d || maximumPublishedSampleCount < 2)
|
zeroSpeedHoldSeconds < 0d || !IsFinite(pathSTolerance) || pathSTolerance < 0d ||
|
||||||
|
maximumPublishedSampleCount < 2)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(configuration));
|
throw new ArgumentOutOfRangeException(nameof(configuration));
|
||||||
}
|
}
|
||||||
@@ -64,7 +68,7 @@ public sealed class EmTrajectoryAssembler
|
|||||||
return EmPlanningStatus.FullSegmentResourceLimitExceeded;
|
return EmPlanningStatus.FullSegmentResourceLimitExceeded;
|
||||||
}
|
}
|
||||||
var schedule = new TrajectorySampleSchedule(longitudinal.Candidate, outputTimeStepSeconds, holdDurationSeconds,
|
var schedule = new TrajectorySampleSchedule(longitudinal.Candidate, outputTimeStepSeconds, holdDurationSeconds,
|
||||||
metadata.LongitudinalMode, isFullDirectionSegment);
|
metadata.LongitudinalMode, isFullDirectionSegment, pathSTolerance);
|
||||||
double terminalPathS = path.Points[path.Points.Count - 1].PathS;
|
double terminalPathS = path.Points[path.Points.Count - 1].PathS;
|
||||||
var points = new List<EmTrajectoryPoint>(schedule.Samples.Count);
|
var points = new List<EmTrajectoryPoint>(schedule.Samples.Count);
|
||||||
double directionSign = metadata.Direction == TravelDirection.Forward ? 1d : -1d;
|
double directionSign = metadata.Direction == TravelDirection.Forward ? 1d : -1d;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ internal sealed class TrajectorySampleSchedule
|
|||||||
private const double ZeroTolerance = 1e-12d;
|
private const double ZeroTolerance = 1e-12d;
|
||||||
|
|
||||||
public TrajectorySampleSchedule(LongitudinalCandidate candidate, double outputTimeStepSeconds, double holdDurationSeconds,
|
public TrajectorySampleSchedule(LongitudinalCandidate candidate, double outputTimeStepSeconds, double holdDurationSeconds,
|
||||||
EmLongitudinalMode mode, bool resampleMotion)
|
EmLongitudinalMode mode, bool resampleMotion, double pathSTolerance)
|
||||||
{
|
{
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
throw new ArgumentNullException(nameof(candidate));
|
throw new ArgumentNullException(nameof(candidate));
|
||||||
@@ -17,11 +18,14 @@ internal sealed class TrajectorySampleSchedule
|
|||||||
throw new ArgumentOutOfRangeException(nameof(outputTimeStepSeconds));
|
throw new ArgumentOutOfRangeException(nameof(outputTimeStepSeconds));
|
||||||
if (!IsFinite(holdDurationSeconds) || holdDurationSeconds < 0d)
|
if (!IsFinite(holdDurationSeconds) || holdDurationSeconds < 0d)
|
||||||
throw new ArgumentOutOfRangeException(nameof(holdDurationSeconds));
|
throw new ArgumentOutOfRangeException(nameof(holdDurationSeconds));
|
||||||
|
if (!IsFinite(pathSTolerance) || pathSTolerance < 0d)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(pathSTolerance));
|
||||||
if (!Enum.IsDefined(typeof(EmLongitudinalMode), mode))
|
if (!Enum.IsDefined(typeof(EmLongitudinalMode), mode))
|
||||||
throw new ArgumentOutOfRangeException(nameof(mode));
|
throw new ArgumentOutOfRangeException(nameof(mode));
|
||||||
|
|
||||||
var samples = new List<TrajectorySample>(candidate.KnotTimes.Count + 4);
|
var samples = new List<TrajectorySample>(candidate.KnotTimes.Count + 4);
|
||||||
double previousPathS = double.NegativeInfinity;
|
double previousPathS = double.NegativeInfinity;
|
||||||
|
double terminalPathS = candidate.S[candidate.S.Count - 1];
|
||||||
if (resampleMotion)
|
if (resampleMotion)
|
||||||
{
|
{
|
||||||
double finalTime = candidate.KnotTimes[candidate.KnotTimes.Count - 1];
|
double finalTime = candidate.KnotTimes[candidate.KnotTimes.Count - 1];
|
||||||
@@ -29,9 +33,11 @@ internal sealed class TrajectorySampleSchedule
|
|||||||
for (double sampleTime = 0d; sampleTime < finalTime - ZeroTolerance;
|
for (double sampleTime = 0d; sampleTime < finalTime - ZeroTolerance;
|
||||||
sampleTime += outputTimeStepSeconds)
|
sampleTime += outputTimeStepSeconds)
|
||||||
{
|
{
|
||||||
AddSample(Interpolate(candidate, sampleTime, ref sourceInterval), samples, ref previousPathS, candidate);
|
AddSample(Interpolate(candidate, sampleTime, ref sourceInterval), samples.Count, samples,
|
||||||
|
ref previousPathS, terminalPathS, pathSTolerance, candidate);
|
||||||
}
|
}
|
||||||
AddSample(Interpolate(candidate, finalTime, ref sourceInterval), samples, ref previousPathS, candidate);
|
AddSample(Interpolate(candidate, finalTime, ref sourceInterval), samples.Count, samples,
|
||||||
|
ref previousPathS, terminalPathS, pathSTolerance, candidate);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -39,7 +45,7 @@ internal sealed class TrajectorySampleSchedule
|
|||||||
{
|
{
|
||||||
AddSample(new TrajectorySample(candidate.KnotTimes[index], candidate.S[index],
|
AddSample(new TrajectorySample(candidate.KnotTimes[index], candidate.S[index],
|
||||||
Math.Max(0d, candidate.U[index]), candidate.A[index], index < candidate.J.Count ? candidate.J[index] : 0d,
|
Math.Max(0d, candidate.U[index]), candidate.A[index], index < candidate.J.Count ? candidate.J[index] : 0d,
|
||||||
false), samples, ref previousPathS, candidate);
|
false), samples.Count, samples, ref previousPathS, terminalPathS, pathSTolerance, candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,17 +136,39 @@ internal sealed class TrajectorySampleSchedule
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddSample(TrajectorySample sample, ICollection<TrajectorySample> samples,
|
private static void AddSample(TrajectorySample sample, int sampleIndex, ICollection<TrajectorySample> samples,
|
||||||
ref double previousPathS, LongitudinalCandidate candidate)
|
ref double previousPathS, double terminalPathS, double pathSTolerance, LongitudinalCandidate candidate)
|
||||||
{
|
{
|
||||||
if (sample.PathS < previousPathS)
|
double lowerDifference = previousPathS - sample.PathS;
|
||||||
throw new ArgumentException("Trajectory PathS cannot decrease.", nameof(candidate));
|
double upperDifference = sample.PathS - terminalPathS;
|
||||||
|
if (lowerDifference > pathSTolerance || upperDifference > pathSTolerance)
|
||||||
|
{
|
||||||
|
throw PathSFailure(sampleIndex, previousPathS, sample.PathS,
|
||||||
|
Math.Max(lowerDifference, upperDifference), candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
double normalizedPathS = Math.Min(terminalPathS, Math.Max(previousPathS, sample.PathS));
|
||||||
|
if (normalizedPathS != sample.PathS)
|
||||||
|
{
|
||||||
|
sample = new TrajectorySample(sample.TimeFromStart, normalizedPathS, sample.ProgressSpeed,
|
||||||
|
sample.Acceleration, sample.Jerk, sample.IsHoldSample);
|
||||||
|
}
|
||||||
if (sample.ProgressSpeed < -ZeroTolerance)
|
if (sample.ProgressSpeed < -ZeroTolerance)
|
||||||
throw new ArgumentException("Longitudinal progress speed cannot be negative.", nameof(candidate));
|
throw new ArgumentException("Longitudinal progress speed cannot be negative.", nameof(candidate));
|
||||||
samples.Add(sample);
|
samples.Add(sample);
|
||||||
previousPathS = sample.PathS;
|
previousPathS = sample.PathS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ArgumentException PathSFailure(int sampleIndex, double previousPathS,
|
||||||
|
double candidatePathS, double difference, LongitudinalCandidate candidate)
|
||||||
|
{
|
||||||
|
return new ArgumentException("Trajectory PathS violates monotonic publication bounds: sampleIndex=" +
|
||||||
|
sampleIndex.ToString(CultureInfo.InvariantCulture) + ";previousPathS=" +
|
||||||
|
previousPathS.ToString("R", CultureInfo.InvariantCulture) + ";candidatePathS=" +
|
||||||
|
candidatePathS.ToString("R", CultureInfo.InvariantCulture) + ";difference=" +
|
||||||
|
difference.ToString("R", CultureInfo.InvariantCulture) + ".", nameof(candidate));
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IncrementAndExceedsMaximum(ref int sampleCount, int maximumSampleCount)
|
private static bool IncrementAndExceedsMaximum(ref int sampleCount, int maximumSampleCount)
|
||||||
{
|
{
|
||||||
checked { sampleCount++; }
|
checked { sampleCount++; }
|
||||||
|
|||||||
Reference in New Issue
Block a user