fix: reject overflowing curvature transitions
This commit is contained in:
@@ -26,6 +26,10 @@ internal sealed class CurvatureTransition
|
|||||||
throw new ArgumentOutOfRangeException(nameof(localArcLengthMeters));
|
throw new ArgumentOutOfRangeException(nameof(localArcLengthMeters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double curvatureJumpPerMeter = Math.Abs(rightVehicleCurvaturePerMeter - leftVehicleCurvaturePerMeter);
|
||||||
|
if (!NumericGuard.IsFinite(curvatureJumpPerMeter))
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(rightVehicleCurvaturePerMeter));
|
||||||
|
|
||||||
SegmentIndex = segmentIndex;
|
SegmentIndex = segmentIndex;
|
||||||
LeftCoarsePathIndex = leftCoarsePathIndex;
|
LeftCoarsePathIndex = leftCoarsePathIndex;
|
||||||
RightCoarsePathIndex = rightCoarsePathIndex;
|
RightCoarsePathIndex = rightCoarsePathIndex;
|
||||||
@@ -35,7 +39,7 @@ internal sealed class CurvatureTransition
|
|||||||
VehicleHeadingRadians = vehicleHeadingRadians;
|
VehicleHeadingRadians = vehicleHeadingRadians;
|
||||||
LeftVehicleCurvaturePerMeter = leftVehicleCurvaturePerMeter;
|
LeftVehicleCurvaturePerMeter = leftVehicleCurvaturePerMeter;
|
||||||
RightVehicleCurvaturePerMeter = rightVehicleCurvaturePerMeter;
|
RightVehicleCurvaturePerMeter = rightVehicleCurvaturePerMeter;
|
||||||
CurvatureJumpPerMeter = Math.Abs(rightVehicleCurvaturePerMeter - leftVehicleCurvaturePerMeter);
|
CurvatureJumpPerMeter = curvatureJumpPerMeter;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int SegmentIndex { get; }
|
internal int SegmentIndex { get; }
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ internal sealed class CurvatureTransitionDetector
|
|||||||
CoarsePathPoint left = request.CoarsePath[leftIndex];
|
CoarsePathPoint left = request.CoarsePath[leftIndex];
|
||||||
CoarsePathPoint right = request.CoarsePath[leftIndex + 1];
|
CoarsePathPoint right = request.CoarsePath[leftIndex + 1];
|
||||||
double delta = right.VehicleCurvature - left.VehicleCurvature;
|
double delta = right.VehicleCurvature - left.VehicleCurvature;
|
||||||
|
if (!NumericGuard.IsFinite(delta))
|
||||||
|
{
|
||||||
|
reason = "局部 G2 曲率事件跳变溢出。";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (Math.Abs(delta) >= threshold)
|
if (Math.Abs(delta) >= threshold)
|
||||||
{
|
{
|
||||||
detected.Add(new CurvatureTransition(
|
detected.Add(new CurvatureTransition(
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ function Assert-True($Actual, [string]$Message) {
|
|||||||
if (-not $Actual) { throw $Message }
|
if (-not $Actual) { throw $Message }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Assert-False($Actual, [string]$Message) {
|
||||||
|
if ($Actual) { throw $Message }
|
||||||
|
}
|
||||||
|
|
||||||
function Assert-Equal($Expected, $Actual, [string]$Message) {
|
function Assert-Equal($Expected, $Actual, [string]$Message) {
|
||||||
if ($Expected -ne $Actual) { throw "$Message Expected=$Expected Actual=$Actual" }
|
if ($Expected -ne $Actual) { throw "$Message Expected=$Expected Actual=$Actual" }
|
||||||
}
|
}
|
||||||
@@ -55,4 +59,38 @@ Assert-Near 0.0 $nearStart.StartArcLength 0.000000001 'A start window must be cl
|
|||||||
Assert-True ($nearStart.RightWindowLength -gt $nearStart.LeftWindowLength) `
|
Assert-True ($nearStart.RightWindowLength -gt $nearStart.LeftWindowLength) `
|
||||||
'Unavailable left length must be shifted to the right.'
|
'Unavailable left length must be shifted to the right.'
|
||||||
|
|
||||||
|
# Finite endpoint curvatures may still overflow during subtraction; detection must fail rather than publish infinity.
|
||||||
|
$coarsePathPointType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.CoarsePath.CoarsePathPoint'
|
||||||
|
$pathSegmentType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.CoarsePath.PathSegment'
|
||||||
|
$directionType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.CoarsePath.TravelDirection'
|
||||||
|
$coarseSourceType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.CoarsePath.CoarsePathPointSource'
|
||||||
|
$requestType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.PathSmoothing.PathSmoothingRequest'
|
||||||
|
$configurationType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.PathSmoothing.PathSmoothingConfiguration'
|
||||||
|
$optionsType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.PathSmoothing.LocalG2.LocalG2OptionsSnapshot'
|
||||||
|
$forward = [Enum]::Parse($directionType, 'Forward')
|
||||||
|
$motionPrimitive = [Enum]::Parse($coarseSourceType, 'MotionPrimitive')
|
||||||
|
$overflowPoints = [Array]::CreateInstance($coarsePathPointType, 2)
|
||||||
|
$overflowPoints.SetValue([Activator]::CreateInstance($coarsePathPointType, @(
|
||||||
|
[double]0.0, [double]0.0, [double]0.0, [double]0.0, [double]0.0, $forward,
|
||||||
|
-[double]::MaxValue, [double]1.0, $false, $motionPrimitive)), 0)
|
||||||
|
$overflowPoints.SetValue([Activator]::CreateInstance($coarsePathPointType, @(
|
||||||
|
[double]0.1, [double]0.0, [double]0.0, [double]0.0, [double]0.1, $forward,
|
||||||
|
[double]::MaxValue, [double]1.0, $false, $motionPrimitive)), 1)
|
||||||
|
$overflowSegments = [Array]::CreateInstance($pathSegmentType, 1)
|
||||||
|
$overflowSegments.SetValue([Activator]::CreateInstance($pathSegmentType, @(0, $forward, 0, 1, $false, $false)), 0)
|
||||||
|
$configuration = [Activator]::CreateInstance($configurationType)
|
||||||
|
$optionsConstructor = $optionsType.GetConstructor([Reflection.BindingFlags]'Instance,NonPublic', $null, @($configurationType), $null)
|
||||||
|
Assert-True ($null -ne $optionsConstructor) 'Local G2 options must be constructible for detector reflection tests.'
|
||||||
|
$options = $optionsConstructor.Invoke(@($configuration))
|
||||||
|
$overflowRequest = [Activator]::CreateInstance($requestType, @($overflowPoints, $overflowSegments, $null, $null, $configuration))
|
||||||
|
$detector = [Activator]::CreateInstance($detectorType, $true)
|
||||||
|
$tryDetect = $detectorType.GetMethod('TryDetect', [Reflection.BindingFlags]'Instance,NonPublic')
|
||||||
|
Assert-True ($null -ne $tryDetect) 'CurvatureTransitionDetector must retain its internal TryDetect contract.'
|
||||||
|
$overflowArguments = [object[]]@($overflowRequest, [double]0.8333, $options, $null, $null)
|
||||||
|
$overflowAccepted = $tryDetect.Invoke($detector, $overflowArguments)
|
||||||
|
Assert-False $overflowAccepted 'Curvature subtraction overflow must reject detection.'
|
||||||
|
Assert-True (-not [string]::IsNullOrWhiteSpace([string]$overflowArguments[4])) `
|
||||||
|
'Rejected overflow detection must provide a stable reason.'
|
||||||
|
Assert-Equal 0 $overflowArguments[3].Count 'Rejected overflow detection must not publish a curvature event.'
|
||||||
|
|
||||||
Write-Output 'Path smoothing Local G2 detection checks passed.'
|
Write-Output 'Path smoothing Local G2 detection checks passed.'
|
||||||
|
|||||||
Reference in New Issue
Block a user