Files
ParkingRobot/ClumsyPilot/tests/EMPlannerVerificationHost/FrenetChecks.cs
T

137 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using EMPlannerVerificationHost;
using MultiWheelC.TrajectoryPlanning.CoarsePath;
using MultiWheelC.TrajectoryPlanning.PathSmoothing;
using MultiWheelC.TrajectoryPlanning.Utils;
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
internal static class FrenetChecks
{
public static void Run()
{
VerifiesForwardProjectionAndReconstruction();
VerifiesReverseProjectionAcrossYawWrap();
VerifiesBoundedProjectionOnALoop();
}
private static void VerifiesForwardProjectionAndReconstruction()
{
DirectionSegmentView segment = CreateStraightSegment(TravelDirection.Forward, 0d, 0d);
var world = new Pose2D(1.5d, 0.2d, 0d);
var projector = new FrenetProjector();
Verification.True(projector.TryProject(world, segment, 0d, 4d, 0.5d, out FrenetProjection projection),
"forward projection succeeds");
Verification.NearlyEqual(1.5d, projection.ReferenceS, "forward reference s");
Verification.NearlyEqual(0.2d, projection.LateralOffset, "forward positive l is travel-left");
Verification.True(FrenetTransform.TryReconstruct(projection.ReferencePoint, projection.LateralOffset, 0d, 0.2d,
out Pose2D reconstructed), "forward reconstruction succeeds");
Verification.NearlyEqual(world.X, reconstructed.X, "forward reconstructed x");
Verification.NearlyEqual(world.Y, reconstructed.Y, "forward reconstructed y");
Verification.NearlyEqual(0d, reconstructed.Heading, "forward reconstructed vehicle yaw");
}
private static void VerifiesReverseProjectionAcrossYawWrap()
{
const double vehicleYaw = -Math.PI + 0.01d;
const double travelYaw = 0.01d;
DirectionSegmentView segment = CreateStraightSegment(TravelDirection.Reverse, vehicleYaw, travelYaw);
double expectedS = 1.5d;
double expectedL = 0.2d;
var world = new Pose2D(
expectedS * Math.Cos(travelYaw) - expectedL * Math.Sin(travelYaw),
expectedS * Math.Sin(travelYaw) + expectedL * Math.Cos(travelYaw),
vehicleYaw);
var projector = new FrenetProjector();
Verification.True(projector.TryProject(world, segment, 0d, 4d, 0.5d, out FrenetProjection projection),
"reverse projection succeeds");
Verification.NearlyEqual(expectedS, projection.ReferenceS, "reverse reference s");
Verification.NearlyEqual(expectedL, projection.LateralOffset, "reverse positive l is travel-left and body-right");
Verification.True(FrenetTransform.TryReconstruct(projection.ReferencePoint, projection.LateralOffset, 0d, 0.2d,
out Pose2D reconstructed), "reverse reconstruction succeeds");
Verification.NearlyEqual(world.X, reconstructed.X, "reverse reconstructed x");
Verification.NearlyEqual(world.Y, reconstructed.Y, "reverse reconstructed y");
Verification.NearlyEqual(AngleMath.NormalizeRadians(vehicleYaw), reconstructed.Heading,
"reverse reconstructed vehicle yaw");
DirectionSegmentView wrapped = CreateWrappedForwardSegment();
FrenetReferencePoint wrappedPoint = ReferencePathInterpolator.Interpolate(wrapped, 1.5d);
Verification.NearlyEqual(3.18d, wrappedPoint.UnwrappedVehicleYaw, "interpolated yaw remains unwrapped internally");
Verification.NearlyEqual(AngleMath.NormalizeRadians(3.18d), wrappedPoint.VehicleYaw,
"interpolated public yaw is normalized");
Verification.True(!FrenetTransform.TryReconstruct(new FrenetReferencePoint(0d, 0d, 0d, 0d, 0d,
TravelDirection.Forward, 1d, 0d, 0d, 0d), 1d, 0d, 0.2d, out _),
"singular Frenet reconstruction is rejected");
}
private static void VerifiesBoundedProjectionOnALoop()
{
DirectionSegmentView segment = CreateLoopSegment();
var projector = new FrenetProjector();
var world = new Pose2D(1d, 0.2d, Math.PI);
Verification.True(projector.TryProject(world, segment, 2.2d, 4.2d, 0.1d, out FrenetProjection projection),
"bounded loop projection succeeds");
Verification.NearlyEqual(3.2d, projection.ReferenceS, "bounded loop picks the local upper branch");
Verification.True(projection.ReferenceS >= 2.2d && projection.ReferenceS <= 4.2d,
"projection remains in supplied reference-s interval");
Verification.True(FrenetTransform.TryReconstruct(projection.ReferencePoint, projection.LateralOffset, 0d, 0.2d,
out Pose2D reconstructed), "loop reconstruction succeeds");
Verification.NearlyEqual(world.X, reconstructed.X, "loop reconstructed x");
Verification.NearlyEqual(world.Y, reconstructed.Y, "loop reconstructed y");
}
private static DirectionSegmentView CreateStraightSegment(TravelDirection direction, double vehicleYaw,
double travelYaw)
{
var points = new List<SmoothedPathPoint>
{
Point(0d, 0d, AngleMath.NormalizeRadians(vehicleYaw), vehicleYaw, 0d, direction),
Point(4d * Math.Cos(travelYaw), 4d * Math.Sin(travelYaw), AngleMath.NormalizeRadians(vehicleYaw),
vehicleYaw, 4d, direction),
};
return CreateSegment(direction, points);
}
private static DirectionSegmentView CreateWrappedForwardSegment()
{
var points = new List<SmoothedPathPoint>
{
Point(0d, 0d, AngleMath.NormalizeRadians(3.12d), 3.12d, 0d, TravelDirection.Forward),
Point(1d, 0d, AngleMath.NormalizeRadians(3.16d), 3.16d, 1d, TravelDirection.Forward),
Point(2d, 0d, AngleMath.NormalizeRadians(3.20d), 3.20d, 2d, TravelDirection.Forward),
};
return CreateSegment(TravelDirection.Forward, points);
}
private static DirectionSegmentView CreateLoopSegment()
{
var points = new List<SmoothedPathPoint>
{
Point(0d, 0d, 0d, 0d, 0d, TravelDirection.Forward),
Point(2d, 0d, 0d, 0d, 2d, TravelDirection.Forward),
Point(2d, 0.2d, Math.PI / 2d, Math.PI / 2d, 2.2d, TravelDirection.Forward),
Point(0d, 0.2d, Math.PI, Math.PI, 4.2d, TravelDirection.Forward),
};
return CreateSegment(TravelDirection.Forward, points);
}
private static DirectionSegmentView CreateSegment(TravelDirection direction, IReadOnlyList<SmoothedPathPoint> points)
{
double length = points[points.Count - 1].ArcLength;
return new DirectionSegmentView(0, direction, points,
new ReferenceBoundary(0, 0d, EmBoundaryType.None, 0d),
new ReferenceBoundary(0, length, EmBoundaryType.Goal, length), 0d);
}
private static SmoothedPathPoint Point(double x, double y, double heading, double unwrappedHeading, double s,
TravelDirection direction)
{
return new SmoothedPathPoint(x, y, heading, unwrappedHeading, s, direction, 0d, 0d, 0d, 1d,
false, SmoothedPathPointSource.Anchor);
}
}