Files
ParkingRobot/ClumsyPilot/tests/verify_coarse_path_collision.ps1
T

131 lines
7.3 KiB
PowerShell

param([string]$AssemblyPath = (Join-Path $PSScriptRoot '..\bin\Debug\netstandard2.0\ClumsyPilot.dll'))
$ErrorActionPreference = 'Stop'
$assembly = [Reflection.Assembly]::LoadFrom((Resolve-Path $AssemblyPath))
function Assert-True($Actual, [string]$Message) {
if (-not $Actual) { throw $Message }
}
function Assert-False($Actual, [string]$Message) {
if ($Actual) { throw $Message }
}
function Assert-Near([double]$Expected, [double]$Actual, [string]$Message) {
if ([Math]::Abs($Expected - $Actual) -gt 0.000001) { throw "$Message Expected=$Expected Actual=$Actual" }
}
function New-TestMap {
$mapping = 'MultiWheelC.TrajectoryPlanning.Mapping.'
$boundsType = $assembly.GetType($mapping + 'MapBoundsMm', $true)
$obstacleType = $assembly.GetType($mapping + 'IMapObstacle', $true)
$rectangleType = $assembly.GetType($mapping + 'AxisAlignedRectangleObstacle', $true)
$sourceType = $assembly.GetType($mapping + 'IMapObstacleSource', $true)
$manualType = $assembly.GetType($mapping + 'ManualObstacleSource', $true)
$requestType = $assembly.GetType($mapping + 'PlanningMapRequest', $true)
$factoryType = $assembly.GetType($mapping + 'PlanningMapFactory', $true)
$bounds = [Activator]::CreateInstance($boundsType, @([single]0, [single]3000, [single]0, [single]3000))
$obstacle = [Activator]::CreateInstance($rectangleType, @([single]1500, [single]1550, [single]1200, [single]1800))
$obstacles = [Array]::CreateInstance($obstacleType, 1)
$obstacles.SetValue($obstacle, 0)
$source = [Activator]::CreateInstance($manualType, @('manual', [long]1, $true, $obstacles))
$sources = [Array]::CreateInstance($sourceType, 1)
$sources.SetValue($source, 0)
$request = [Activator]::CreateInstance($requestType)
$request.Bounds = $bounds
$request.ResolutionMm = [single]50
$request.ObstacleSources = $sources
$request.AllowExplicitEmptyMap = $false
$result = [Activator]::CreateInstance($factoryType).Create($request)
Assert-True $result.Succeeded 'Test map must be created.'
Assert-True $result.Map.PlanningReady 'Test map must be ready.'
return $result.Map
}
function New-DiagonalCellMap {
$mapping = 'MultiWheelC.TrajectoryPlanning.Mapping.'
$boundsType = $assembly.GetType($mapping + 'MapBoundsMm', $true)
$obstacleType = $assembly.GetType($mapping + 'IMapObstacle', $true)
$circleType = $assembly.GetType($mapping + 'CircleObstacle', $true)
$sourceType = $assembly.GetType($mapping + 'IMapObstacleSource', $true)
$manualType = $assembly.GetType($mapping + 'ManualObstacleSource', $true)
$requestType = $assembly.GetType($mapping + 'PlanningMapRequest', $true)
$factoryType = $assembly.GetType($mapping + 'PlanningMapFactory', $true)
$bounds = [Activator]::CreateInstance($boundsType, @([single]0, [single]3000, [single]0, [single]3000))
$obstacle = [Activator]::CreateInstance($circleType, @([single]1525, [single]1525, [single]0))
$obstacles = [Array]::CreateInstance($obstacleType, 1)
$obstacles.SetValue($obstacle, 0)
$source = [Activator]::CreateInstance($manualType, @('diagonal-cell', [long]1, $true, $obstacles))
$sources = [Array]::CreateInstance($sourceType, 1)
$sources.SetValue($source, 0)
$request = [Activator]::CreateInstance($requestType)
$request.Bounds = $bounds
$request.ResolutionMm = [single]50
$request.ObstacleSources = $sources
$request.AllowExplicitEmptyMap = $false
$result = [Activator]::CreateInstance($factoryType).Create($request)
Assert-True $result.Succeeded 'Diagonal cell map must be created.'
Assert-True $result.Map.PlanningReady 'Diagonal cell map must be ready.'
Assert-True $result.Map.IsOccupied(30, 30) 'Diagonal cell obstacle must occupy its 50mm cell.'
return $result.Map
}
$map = New-TestMap
$vehicle = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.VehicleParameters
$vehicle.LengthMeters = 0.20
$vehicle.WidthMeters = 0.20
$vehicle.SafetyMarginMeters = 0.0
$checker = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Vehicle.FootprintCollisionChecker
$diagonalCellMap = New-DiagonalCellMap
$diagonalCellVehicle = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.VehicleParameters
$diagonalCellVehicle.LengthMeters = 0.10
$diagonalCellVehicle.WidthMeters = 0.10
$diagonalCellVehicle.SafetyMarginMeters = 0.0
$diagonalCellPose = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(1.299, 1.299, 0.0)
$diagonalCellClearance = 0.0
$diagonalCellSafe = $checker.IsPoseCollisionFree($diagonalCellPose, $diagonalCellMap, $diagonalCellVehicle, 0.20, [ref]$diagonalCellClearance)
Assert-False $diagonalCellSafe 'Expanded diagonal footprint must not bypass an occupied 50mm cell.'
$edgePose = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(1.35, 1.50, 0.0)
$edgeClearance = 0.0
$edgeSafe = $checker.IsPoseCollisionFree($edgePose, $map, $vehicle, 0.0, [ref]$edgeClearance)
Assert-False $edgeSafe 'Touching an occupied cell must be a collision.'
$farPose = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(0.50, 0.50, 0.0)
$farClearance = 0.0
$farSafe = $checker.IsPoseCollisionFree($farPose, $map, $vehicle, 0.0, [ref]$farClearance)
Assert-True $farSafe 'Strict distance field clearance must allow the distant pose.'
Assert-True ($farClearance -gt 0.0) 'Distant pose must report positive body clearance.'
$from = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(1.20, 1.50, 0.0)
$to = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(1.80, 1.50, 0.0)
$sweepClearance = 0.0
$sweepSafe = $checker.IsSweptMotionCollisionFree($from, $to, $map, $vehicle, 0.50, [ref]$sweepClearance)
Assert-False $sweepSafe 'A swept vehicle must not pass through an occupied cell.'
$gridCenterPose = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(1.475, 1.475, 0.0)
$gridCenterClearance = 0.0
Assert-False $checker.IsPoseCollisionFree($gridCenterPose, $map, $vehicle, 0.0, [ref]$gridCenterClearance) 'A pose at an occupied grid center must collide.'
$subGridPose = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(1.481, 1.463, 0.37)
$subGridClearance = 0.0
Assert-False $checker.IsPoseCollisionFree($subGridPose, $map, $vehicle, 0.0, [ref]$subGridClearance) 'An arbitrary sub-grid heading must collide with the thin obstacle.'
$diagonalPose = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(1.32, 1.50, ([Math]::PI / 4.0))
$diagonalClearance = 0.0
Assert-False $checker.IsPoseCollisionFree($diagonalPose, $map, $vehicle, 0.0, [ref]$diagonalClearance) 'A 45 degree footprint overlap must collide.'
$outsidePose = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.Pose2D(0.05, 0.05, 0.0)
$outsideClearance = 0.0
Assert-False $checker.IsPoseCollisionFree($outsidePose, $map, $vehicle, 0.0, [ref]$outsideClearance) 'Any footprint corner outside the map must collide.'
$curveVehicle = New-Object MultiWheelC.TrajectoryPlanning.CoarsePath.VehicleParameters
$curveVehicle.MaximumCurvaturePerMeter = 0.80
$curveVehicle.MinimumTurningRadiusMeters = 2.0
$maximumCurvature = 0.0
$hasMaximumCurvature = [MultiWheelC.TrajectoryPlanning.CoarsePath.Vehicle.VehicleKinematics]::TryGetMaximumCurvaturePerMeter($curveVehicle, [ref]$maximumCurvature)
Assert-True $hasMaximumCurvature 'Vehicle curvature constraints must be accepted.'
Assert-Near 0.50 $maximumCurvature 'Both curvature constraints must use the conservative minimum.'
Write-Output 'Coarse path collision checks passed.'