feat: 发布 EM 轨迹规划首个版本
This commit is contained in:
+217
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
internal sealed class LongitudinalQpAuditResult
|
||||
{
|
||||
internal LongitudinalQpAuditResult(bool isFeasible, double maximumResidual, int worstRow,
|
||||
string category, string unit)
|
||||
{
|
||||
IsFeasible = isFeasible;
|
||||
MaximumResidual = maximumResidual;
|
||||
WorstRow = worstRow;
|
||||
Category = category;
|
||||
Unit = unit;
|
||||
}
|
||||
|
||||
internal bool IsFeasible { get; }
|
||||
|
||||
internal double MaximumResidual { get; }
|
||||
|
||||
internal int WorstRow { get; }
|
||||
|
||||
internal string Category { get; }
|
||||
|
||||
internal string Unit { get; }
|
||||
}
|
||||
|
||||
internal static class LongitudinalQpFeasibilityAudit
|
||||
{
|
||||
internal static LongitudinalQpAuditResult Evaluate(QuadraticProgram problem,
|
||||
LongitudinalCandidate candidate, double tolerance, LongitudinalVariableLayout layout,
|
||||
int stabilizationStart)
|
||||
{
|
||||
if (problem == null)
|
||||
throw new ArgumentNullException(nameof(problem));
|
||||
if (candidate == null)
|
||||
throw new ArgumentNullException(nameof(candidate));
|
||||
if (layout == null)
|
||||
throw new ArgumentNullException(nameof(layout));
|
||||
if (problem.VariableCount != layout.VariableCount || candidate.S.Count != layout.KnotCount ||
|
||||
candidate.U.Count != layout.KnotCount || candidate.A.Count != layout.KnotCount ||
|
||||
candidate.J.Count != layout.KnotCount - 1)
|
||||
{
|
||||
throw new ArgumentException("The QP, candidate, and longitudinal layout must have matching dimensions.");
|
||||
}
|
||||
|
||||
double[] primal = ToPrimal(candidate, layout);
|
||||
var activity = new double[problem.ConstraintCount];
|
||||
bool allFinite = IsFinite(tolerance);
|
||||
SparseCscMatrix matrix = problem.ConstraintMatrix;
|
||||
for (int column = 0; column < matrix.ColumnCount; column++)
|
||||
{
|
||||
double value = primal[column];
|
||||
allFinite &= IsFinite(value);
|
||||
for (int entry = matrix.ColumnPointers[column]; entry < matrix.ColumnPointers[column + 1]; entry++)
|
||||
activity[matrix.RowIndices[entry]] += matrix.Values[entry] * value;
|
||||
}
|
||||
|
||||
double maximumResidual = 0d;
|
||||
int worstRow = problem.ConstraintCount == 0 ? -1 : 0;
|
||||
for (int row = 0; row < problem.ConstraintCount; row++)
|
||||
{
|
||||
double residual;
|
||||
if (!IsFinite(activity[row]))
|
||||
{
|
||||
allFinite = false;
|
||||
residual = double.PositiveInfinity;
|
||||
}
|
||||
else
|
||||
{
|
||||
residual = Math.Max(0d, Math.Max(
|
||||
problem.LowerBounds[row] - activity[row],
|
||||
activity[row] - problem.UpperBounds[row]));
|
||||
}
|
||||
if (row == 0 || residual > maximumResidual)
|
||||
{
|
||||
maximumResidual = residual;
|
||||
worstRow = row;
|
||||
}
|
||||
}
|
||||
|
||||
DescribeRow(worstRow, layout.KnotCount, stabilizationStart, out string category, out string unit);
|
||||
return new LongitudinalQpAuditResult(allFinite && maximumResidual <= tolerance,
|
||||
maximumResidual, worstRow, category, unit);
|
||||
}
|
||||
|
||||
private static double[] ToPrimal(LongitudinalCandidate candidate, LongitudinalVariableLayout layout)
|
||||
{
|
||||
var primal = new double[layout.VariableCount];
|
||||
for (int index = 0; index < layout.KnotCount; index++)
|
||||
{
|
||||
primal[layout.S(index)] = candidate.S[index];
|
||||
primal[layout.U(index)] = candidate.U[index];
|
||||
primal[layout.A(index)] = candidate.A[index];
|
||||
}
|
||||
for (int index = 0; index < layout.KnotCount - 1; index++)
|
||||
primal[layout.J(index)] = candidate.J[index];
|
||||
return primal;
|
||||
}
|
||||
|
||||
private static void DescribeRow(int targetRow, int knotCount, int stabilizationStart,
|
||||
out string category, out string unit)
|
||||
{
|
||||
int row = 0;
|
||||
for (int index = 0; index < knotCount; index++)
|
||||
{
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "PathS trust";
|
||||
unit = "m";
|
||||
return;
|
||||
}
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "speed";
|
||||
unit = "m/s";
|
||||
return;
|
||||
}
|
||||
if (index > 0 && targetRow == row++)
|
||||
{
|
||||
category = "speed envelope";
|
||||
unit = "m/s";
|
||||
return;
|
||||
}
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "acceleration";
|
||||
unit = "m/s^2";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < knotCount - 1; index++)
|
||||
{
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "jerk";
|
||||
unit = "m/s^3";
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int index = 0; index < knotCount; index++)
|
||||
{
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "low-speed deceleration release";
|
||||
unit = "m/s";
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int index = 0; index < knotCount - 1; index++)
|
||||
{
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "monotonic progress";
|
||||
unit = "m";
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int index = 0; index < knotCount - 1; index++)
|
||||
{
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "exact dynamics";
|
||||
unit = "m/s^2";
|
||||
return;
|
||||
}
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "exact dynamics";
|
||||
unit = "m/s";
|
||||
return;
|
||||
}
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "exact dynamics";
|
||||
unit = "m";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string[] stateUnits = { "m", "m/s", "m/s^2" };
|
||||
for (int state = 0; state < stateUnits.Length; state++)
|
||||
{
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "exact start";
|
||||
unit = stateUnits[state];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int stopKnotCount = stabilizationStart >= 0 && stabilizationStart < knotCount
|
||||
? knotCount - stabilizationStart
|
||||
: 0;
|
||||
for (int index = 0; index < stopKnotCount; index++)
|
||||
{
|
||||
for (int state = 0; state < stateUnits.Length; state++)
|
||||
{
|
||||
if (targetRow == row++)
|
||||
{
|
||||
category = "exact stop";
|
||||
unit = stateUnits[state];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
category = "row accounting";
|
||||
unit = string.Empty;
|
||||
}
|
||||
|
||||
private static bool IsFinite(double value)
|
||||
{
|
||||
return !double.IsNaN(value) && !double.IsInfinity(value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user