Files
ParkingRobot/ClumsyPilot/ParkrobTrajplanner/tarjplanner_movementtest/TrajectoryObservationKinematicChartBuilder.cs
T

180 lines
9.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using MultiWheelC.TrajectoryPlanning.CoarsePath;
using MultiWheelC.TrajectoryPlanning.EMPlanner;
using TrajectoryPlanningVisualization;
namespace MultiWheelC.TrajectoryPlanning.TrajectoryObservation;
public sealed class TrajectoryObservationKinematicChartBuilder
{
public IReadOnlyList<VisualizationChart> Build(EmTrajectory trajectory, DirectionSegmentView segment,
VehicleParameters vehicle, EmPlannerConfiguration configuration)
{
if (trajectory == null) throw new ArgumentNullException(nameof(trajectory));
if (segment == null) throw new ArgumentNullException(nameof(segment));
if (vehicle == null) throw new ArgumentNullException(nameof(vehicle));
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
LsBuildResult ls = BuildLs(trajectory, segment, configuration.Frenet.MaximumProjectionDistanceMeters);
double curvatureLimit = CurvatureLimit(vehicle);
double terminalTime = trajectory.Points.Count == 0 ? 0d : trajectory.Points[trajectory.Points.Count - 1].TimeFromStart;
return new[]
{
Chart("ls", "横向偏移", "ReferenceS (m)", "l (m)", ls.Points,
"完整活动方向段投影;投影失败=" + ls.ProjectionFailureCount.ToString(CultureInfo.InvariantCulture),
new[] { new VisualizationChartAnnotation("ls-s-end", "s-end", "s_end",
segment.SourceStartArcLength + segment.LengthMeters) }),
Chart("st", "时空轨迹", "t (s)", "PathS (m)", Points(trajectory, p => p.PathS), "",
new[] { new VisualizationChartAnnotation("st-s-end", "s-end", "s_end", terminalTime) }),
WithSymmetricLimit("curvature-s", "曲率-距离", "s (m)", "κ (m⁻¹)",
PointsBySegmentS(trajectory), curvatureLimit),
WithSymmetricLimit("curvature-t", "曲率-时间", "t (s)", "κ (m⁻¹)",
Points(trajectory, p => p.VehicleCurvature), curvatureLimit),
Velocity(trajectory, configuration),
Acceleration(trajectory, configuration),
Jerk(trajectory, configuration),
Chart("yaw-rate-t", "横摆角速度", "t (s)", "ω (rad/s)",
Points(trajectory, p => p.YawRate), "生效配置没有独立横摆角速度上限"),
};
}
public int CountProjectionFailures(EmTrajectory trajectory, DirectionSegmentView segment,
EmPlannerConfiguration configuration)
{
if (trajectory == null) throw new ArgumentNullException(nameof(trajectory));
if (segment == null) throw new ArgumentNullException(nameof(segment));
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
return BuildLs(trajectory, segment, configuration.Frenet.MaximumProjectionDistanceMeters).ProjectionFailureCount;
}
private static VisualizationChart Velocity(EmTrajectory trajectory, EmPlannerConfiguration configuration)
{
IReadOnlyList<VisualizationPoint> points = Points(trajectory, p => p.SignedLongitudinalVelocity);
return new VisualizationChart("velocity-t", "速度", "t (s)", "v (m/s)", new[]
{
new VisualizationSeries("velocity-t-current", "当前轨迹", VisualizationLineStyle.Solid, points),
Limit("velocity-t-forward-limit", "前进生效限制", points, configuration.Longitudinal.MaximumForwardSpeedMetersPerSecond),
Limit("velocity-t-reverse-limit", "倒车生效限制", points, -configuration.Longitudinal.MaximumReverseSpeedMetersPerSecond),
});
}
private static VisualizationChart Acceleration(EmTrajectory trajectory, EmPlannerConfiguration configuration)
{
IReadOnlyList<VisualizationPoint> points = Points(trajectory, p => p.LongitudinalAcceleration);
return new VisualizationChart("acceleration-t", "加速度", "t (s)", "a (m/s²)", new[]
{
new VisualizationSeries("acceleration-t-current", "当前轨迹", VisualizationLineStyle.Solid, points),
Limit("acceleration-t-positive-limit", "加速生效限制", points, configuration.Longitudinal.MaximumAccelerationMetersPerSecondSquared),
Limit("acceleration-t-negative-limit", "减速生效限制", points, -configuration.Longitudinal.MaximumDecelerationMetersPerSecondSquared),
});
}
private static VisualizationChart Jerk(EmTrajectory trajectory, EmPlannerConfiguration configuration)
{
var intervals = new List<VisualizationPoint>();
for (int index = 0; index + 1 < trajectory.Points.Count; index++)
intervals.Add(new VisualizationPoint(trajectory.Points[index].TimeFromStart,
trajectory.Points[index].LongitudinalJerk));
return new VisualizationChart("jerk-t", "加加速度", "t (s)", "j (m/s³)", new[]
{
new VisualizationSeries("jerk-t-current", "当前轨迹", VisualizationLineStyle.Solid, intervals),
Limit("jerk-t-positive-limit", "正向生效限制", intervals, configuration.Longitudinal.MaximumJerkMetersPerSecondCubed),
Limit("jerk-t-negative-limit", "负向生效限制", intervals, -configuration.Longitudinal.MaximumJerkMetersPerSecondCubed),
}, "j[i] 表示 [tᵢ, tᵢ₊₁);末点后无时间区间");
}
private static VisualizationChart WithSymmetricLimit(string id, string title, string xAxis, string yAxis,
IReadOnlyList<VisualizationPoint> points, double limit)
{
return new VisualizationChart(id, title, xAxis, yAxis, new[]
{
new VisualizationSeries(id + "-current", "当前轨迹", VisualizationLineStyle.Solid, points),
Limit(id + "-positive-limit", "生效限制", points, limit),
Limit(id + "-negative-limit", "生效限制", points, -limit),
});
}
private static VisualizationSeries Limit(string id, string legend, IReadOnlyList<VisualizationPoint> points, double y)
{
double end = points.Count == 0 ? 0d : points[points.Count - 1].X;
return new VisualizationSeries(id, legend, VisualizationLineStyle.Limit,
new[] { new VisualizationPoint(0d, y), new VisualizationPoint(end, y) });
}
private static VisualizationChart Chart(string id, string title, string xAxis, string yAxis,
IReadOnlyList<VisualizationPoint> points, string note,
IReadOnlyList<VisualizationChartAnnotation> annotations = null)
{
return new VisualizationChart(id, title, xAxis, yAxis,
new[] { new VisualizationSeries(id + "-current", "当前轨迹", VisualizationLineStyle.Solid, points) },
note, annotations ?? Array.Empty<VisualizationChartAnnotation>());
}
private static IReadOnlyList<VisualizationPoint> Points(EmTrajectory trajectory, Func<EmTrajectoryPoint, double> y)
{
var points = new List<VisualizationPoint>();
foreach (EmTrajectoryPoint point in trajectory.Points)
points.Add(new VisualizationPoint(point.TimeFromStart, y(point)));
return points;
}
private static IReadOnlyList<VisualizationPoint> PointsBySegmentS(EmTrajectory trajectory)
{
var points = new List<VisualizationPoint>();
foreach (EmTrajectoryPoint point in trajectory.Points)
points.Add(new VisualizationPoint(point.SegmentLocalS, point.VehicleCurvature));
return points;
}
private static double CurvatureLimit(VehicleParameters vehicle)
{
if (vehicle.MaximumCurvaturePerMeter.HasValue)
{
return Math.Abs(vehicle.MaximumCurvaturePerMeter.Value);
}
if (vehicle.MinimumTurningRadiusMeters.HasValue && vehicle.MinimumTurningRadiusMeters.Value > 0d)
{
return 1d / vehicle.MinimumTurningRadiusMeters.Value;
}
throw new ArgumentException("Vehicle curvature limit is required.", nameof(vehicle));
}
private static LsBuildResult BuildLs(EmTrajectory trajectory, DirectionSegmentView segment,
double maximumProjectionDistanceMeters)
{
var points = new List<VisualizationPoint>();
var projector = new FrenetProjector();
double seed = 0d;
int failures = 0;
foreach (EmTrajectoryPoint point in trajectory.Points)
{
if (!projector.TryProject(new Pose2D(point.X, point.Y, point.Yaw), segment, 0d, segment.LengthMeters,
maximumProjectionDistanceMeters, seed, out FrenetProjection projection))
{
failures++;
continue;
}
points.Add(new VisualizationPoint(segment.SourceStartArcLength + projection.ReferenceS,
projection.LateralOffset));
seed = projection.ReferenceS;
}
return new LsBuildResult(points, failures);
}
private sealed class LsBuildResult
{
public LsBuildResult(IReadOnlyList<VisualizationPoint> points, int projectionFailureCount)
{
Points = points;
ProjectionFailureCount = projectionFailureCount;
}
public IReadOnlyList<VisualizationPoint> Points { get; }
public int ProjectionFailureCount { get; }
}
}