feat: 发布 EM 轨迹规划首个版本
This commit is contained in:
@@ -2,8 +2,14 @@ using System;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 验证规划契约中必须为有限数的标量输入。
|
||||
/// </summary>
|
||||
internal static class ContractNumeric
|
||||
{
|
||||
/// <summary>
|
||||
/// 拒绝 NaN 或无穷数值。<paramref name="value"/> 的单位由调用方语境决定;<paramref name="parameterName"/> 原样传给异常构造器,方法本身不验证它。
|
||||
/// </summary>
|
||||
public static void RequireFinite(double value, string parameterName)
|
||||
{
|
||||
if (double.IsNaN(value) || double.IsInfinity(value))
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 EM 规划中参考段终端或滚动窗口终端的边界类型。
|
||||
/// </summary>
|
||||
public enum EmBoundaryType
|
||||
{
|
||||
/// <summary>
|
||||
/// 非边界采样点;不携带终端或换挡边界语义。
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// 滚动窗口因安全约束形成的停车终点,而非方向段的真实终点。
|
||||
/// </summary>
|
||||
RollingSafetyStop,
|
||||
/// <summary>
|
||||
/// 到达换挡位置前的真实停车边界。
|
||||
/// </summary>
|
||||
GearSwitchApproach,
|
||||
/// <summary>
|
||||
/// 换挡后新方向段开始时的离开边界。
|
||||
/// </summary>
|
||||
GearSwitchDeparture,
|
||||
/// <summary>
|
||||
/// 整条参考路径或当前任务目标的真实终点边界。
|
||||
/// </summary>
|
||||
Goal,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 指定纵向规划面对当前窗口末端时采用的速度终端语义。
|
||||
/// </summary>
|
||||
public enum EmLongitudinalMode
|
||||
{
|
||||
/// <summary>
|
||||
/// 普通滚动窗口末端;保持连续行驶,不要求在本窗口内停止。
|
||||
/// </summary>
|
||||
RollingContinuation,
|
||||
/// <summary>
|
||||
/// 将真实停车边界纳入窗口,但在可达性不足时仅以可停车方式接近。
|
||||
/// </summary>
|
||||
ApproachStopBoundary,
|
||||
/// <summary>
|
||||
/// 要求在当前窗口内到达真实边界并保持零速。
|
||||
/// </summary>
|
||||
ExactStopAtBoundary,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 请求允许的车辆运动模型;服务依据此值选择或拒绝相应规划分支。
|
||||
/// </summary>
|
||||
public enum EmMotionModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 常规非完整约束车辆,可前进或倒车但不能侧移。
|
||||
/// </summary>
|
||||
NonholonomicForwardReverse,
|
||||
/// <summary>
|
||||
/// 蟹行平移模型;当前 EM 管线不支持时返回明确状态。
|
||||
/// </summary>
|
||||
CrabTranslation,
|
||||
/// <summary>
|
||||
/// 原地旋转模型;当前 EM 管线不支持时返回明确状态。
|
||||
/// </summary>
|
||||
InPlaceRotation,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,136 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using MultiWheelC.TrajectoryPlanning.CoarsePath;
|
||||
using MultiWheelC.TrajectoryPlanning.Mapping;
|
||||
using MultiWheelC.TrajectoryPlanning.PathSmoothing;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
internal interface IEmPlanningPublicationAuthorization
|
||||
{
|
||||
TimeSpan Remaining { get; }
|
||||
|
||||
bool IsExpired { get; }
|
||||
|
||||
EmPlanningPublicationDecision TryPublish(CancellationToken requestCallerCancellationToken,
|
||||
CancellationToken coordinatorCallerCancellationToken, Action publish);
|
||||
}
|
||||
|
||||
internal enum EmPlanningPublicationDecision
|
||||
{
|
||||
Published = 0,
|
||||
CallerCancelled = 1,
|
||||
DeadlineExpired = 2,
|
||||
}
|
||||
|
||||
internal sealed class EmPlanningRequestPublicationAuthorization : IEmPlanningPublicationAuthorization
|
||||
{
|
||||
private readonly object gate = new object();
|
||||
private readonly TimeSpan? initialRemaining;
|
||||
private readonly Stopwatch stopwatch;
|
||||
private readonly CancellationToken deadlineToken;
|
||||
private bool callerCancelled;
|
||||
private bool deadlineExpired;
|
||||
|
||||
public EmPlanningRequestPublicationAuthorization(TimeSpan? remaining, CancellationToken deadlineToken)
|
||||
{
|
||||
initialRemaining = remaining;
|
||||
this.deadlineToken = deadlineToken;
|
||||
stopwatch = remaining.HasValue ? Stopwatch.StartNew() : null;
|
||||
deadlineExpired = remaining.HasValue && remaining.Value <= TimeSpan.Zero ||
|
||||
deadlineToken.IsCancellationRequested;
|
||||
}
|
||||
|
||||
public TimeSpan Remaining
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
ObserveDeadlineInsideGate();
|
||||
return ComputeRemainingInsideGate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExpired
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
ObserveDeadlineInsideGate();
|
||||
return deadlineExpired;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EmPlanningPublicationDecision TryPublish(CancellationToken requestCallerCancellationToken,
|
||||
CancellationToken coordinatorCallerCancellationToken, Action publish)
|
||||
{
|
||||
if (publish == null)
|
||||
throw new ArgumentNullException(nameof(publish));
|
||||
|
||||
using CancellationTokenRegistration requestCallerRegistration =
|
||||
requestCallerCancellationToken.Register(ObserveCallerCancellation);
|
||||
using CancellationTokenRegistration coordinatorCallerRegistration =
|
||||
coordinatorCallerCancellationToken.Register(ObserveCallerCancellation);
|
||||
using CancellationTokenRegistration deadlineRegistration =
|
||||
deadlineToken.Register(ObserveDeadlineCancellation);
|
||||
lock (gate)
|
||||
{
|
||||
if (requestCallerCancellationToken.IsCancellationRequested ||
|
||||
coordinatorCallerCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
callerCancelled = true;
|
||||
}
|
||||
ObserveDeadlineInsideGate();
|
||||
if (callerCancelled)
|
||||
return EmPlanningPublicationDecision.CallerCancelled;
|
||||
if (deadlineExpired)
|
||||
return EmPlanningPublicationDecision.DeadlineExpired;
|
||||
publish();
|
||||
return EmPlanningPublicationDecision.Published;
|
||||
}
|
||||
}
|
||||
|
||||
private void ObserveCallerCancellation()
|
||||
{
|
||||
lock (gate)
|
||||
callerCancelled = true;
|
||||
}
|
||||
|
||||
private void ObserveDeadlineCancellation()
|
||||
{
|
||||
lock (gate)
|
||||
deadlineExpired = true;
|
||||
}
|
||||
|
||||
private void ObserveDeadlineInsideGate()
|
||||
{
|
||||
if (deadlineToken.IsCancellationRequested || ComputeRemainingInsideGate() <= TimeSpan.Zero)
|
||||
deadlineExpired = true;
|
||||
}
|
||||
|
||||
private TimeSpan ComputeRemainingInsideGate()
|
||||
{
|
||||
if (!initialRemaining.HasValue)
|
||||
return deadlineExpired ? TimeSpan.Zero : TimeSpan.MaxValue;
|
||||
TimeSpan remaining = initialRemaining.Value - stopwatch.Elapsed;
|
||||
return remaining > TimeSpan.Zero && !deadlineExpired ? remaining : TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EM 单次规划的只读输入引用快照,绑定平滑参考路径、地图、车辆状态、目标方向段和输出身份。
|
||||
/// 坐标使用 m,航向使用 rad,时间使用 UTC;调用方必须在进入服务前保证这些输入属于同一业务版本。
|
||||
/// </summary>
|
||||
public sealed class EmPlanningRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存一次规划所需的引用快照和发布身份。构造器只赋值而不校验、复制或取得对象所有权;调用方仍拥有传入引用,服务随后负责验证并复制配置。
|
||||
/// </summary>
|
||||
public EmPlanningRequest(
|
||||
PathSmoothingResult referencePath,
|
||||
PlanningGridMap map,
|
||||
@@ -21,7 +145,10 @@ public sealed class EmPlanningRequest
|
||||
string referencePathId,
|
||||
string previousTrajectoryId,
|
||||
EmMotionModel motionModel,
|
||||
EmPlanningScope planningScope)
|
||||
EmPlanningScope planningScope,
|
||||
TimeSpan? cycleDeadlineRemaining = null,
|
||||
CancellationToken callerCancellationToken = default,
|
||||
CancellationToken cycleDeadlineToken = default)
|
||||
{
|
||||
ReferencePath = referencePath;
|
||||
Map = map;
|
||||
@@ -37,25 +164,166 @@ public sealed class EmPlanningRequest
|
||||
PreviousTrajectoryId = previousTrajectoryId;
|
||||
MotionModel = motionModel;
|
||||
PlanningScope = planningScope;
|
||||
CycleDeadlineRemaining = cycleDeadlineRemaining;
|
||||
CallerCancellationToken = callerCancellationToken;
|
||||
CycleDeadlineToken = cycleDeadlineToken;
|
||||
PublicationAuthorization = new EmPlanningRequestPublicationAuthorization(
|
||||
cycleDeadlineRemaining, cycleDeadlineToken);
|
||||
}
|
||||
|
||||
internal EmPlanningRequest(
|
||||
PathSmoothingResult referencePath,
|
||||
PlanningGridMap map,
|
||||
VehicleParameters vehicle,
|
||||
VehicleMotionState vehicleState,
|
||||
EmPlannerConfiguration configuration,
|
||||
int segmentIndex,
|
||||
EmTrajectory previousTrajectory,
|
||||
DateTimeOffset requestedAtUtc,
|
||||
DateTimeOffset effectiveAtUtc,
|
||||
string outputTrajectoryId,
|
||||
string referencePathId,
|
||||
string previousTrajectoryId,
|
||||
EmMotionModel motionModel,
|
||||
EmPlanningScope planningScope,
|
||||
TimeSpan? cycleDeadlineRemaining,
|
||||
CancellationToken callerCancellationToken,
|
||||
CancellationToken cycleDeadlineToken,
|
||||
IEmPlanningPublicationAuthorization publicationAuthorization)
|
||||
: this(referencePath, map, vehicle, vehicleState, configuration, segmentIndex, previousTrajectory,
|
||||
requestedAtUtc, effectiveAtUtc, outputTrajectoryId, referencePathId, previousTrajectoryId,
|
||||
motionModel, planningScope, cycleDeadlineRemaining, callerCancellationToken, cycleDeadlineToken)
|
||||
{
|
||||
PublicationAuthorization = publicationAuthorization ??
|
||||
new EmPlanningRequestPublicationAuthorization(cycleDeadlineRemaining, cycleDeadlineToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 平滑后的参考路径结果引用;服务要求其为可消费的成功结果,且调用方负责保持其与其他输入版本一致。
|
||||
/// </summary>
|
||||
public PathSmoothingResult ReferencePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 规划使用的栅格地图快照引用;地图坐标单位和快照身份由地图契约定义,构造器不检查 null 或就绪状态。
|
||||
/// </summary>
|
||||
public PlanningGridMap Map { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆几何与运动限制引用;调用方拥有该对象,服务在请求验证后使用其约束。
|
||||
/// </summary>
|
||||
public VehicleParameters Vehicle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 一次性捕获的车辆运动状态;其位置为世界坐标 m、航向为 rad,时效和数值有效性由服务验证。
|
||||
/// </summary>
|
||||
public VehicleMotionState VehicleState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 本次规划的配置引用;请求不深拷贝它,验证通过后服务取得配置副本供本次规划使用。
|
||||
/// </summary>
|
||||
public EmPlannerConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 参考路径中待规划的方向段索引;必须由服务验证为有效的非负范围。
|
||||
/// </summary>
|
||||
public int SegmentIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 可选的上一条已发布轨迹引用,用于衔接或诊断;构造器允许为 null,调用方保有其所有权。
|
||||
/// </summary>
|
||||
public EmTrajectory PreviousTrajectory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 本次规划请求发起的 UTC 时刻;用于车辆状态时效判断和生成轨迹元数据,构造器不验证其时间关系。
|
||||
/// </summary>
|
||||
public DateTimeOffset RequestedAtUtc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 成功发布轨迹开始生效的 UTC 时刻;由下游消费者依据其调度语义使用。
|
||||
/// </summary>
|
||||
public DateTimeOffset EffectiveAtUtc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 成功轨迹的发布标识;构造器不校验,轨迹元数据构造时要求为非空白字符串。
|
||||
/// </summary>
|
||||
public string OutputTrajectoryId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 输入参考路径的业务标识,用于成功轨迹的来源追踪;构造器不校验其 null 或空白值。
|
||||
/// </summary>
|
||||
public string ReferencePathId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 上一条轨迹的业务标识;允许为 null,成功轨迹元数据会将 null 规范化为空字符串。
|
||||
/// </summary>
|
||||
public string PreviousTrajectoryId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求的车辆运动模型;不支持或无效的枚举值由服务转换为失败状态。
|
||||
/// </summary>
|
||||
public EmMotionModel MotionModel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 本次请求覆盖滚动窗口或整个方向段的范围;服务负责验证其枚举值和资源约束。
|
||||
/// </summary>
|
||||
public EmPlanningScope PlanningScope { get; }
|
||||
|
||||
/// <summary>Optional remaining wall-clock budget for the complete outer planning cycle.</summary>
|
||||
public TimeSpan? CycleDeadlineRemaining { get; }
|
||||
|
||||
/// <summary>Dynamic caller-cancellation origin used to preserve terminal-status precedence.</summary>
|
||||
public CancellationToken CallerCancellationToken { get; }
|
||||
|
||||
/// <summary>Dynamic shared-cycle deadline origin checked again at atomic publication.</summary>
|
||||
public CancellationToken CycleDeadlineToken { get; }
|
||||
|
||||
internal IEmPlanningPublicationAuthorization PublicationAuthorization { get; }
|
||||
|
||||
internal bool IsCycleDeadlineExpired()
|
||||
{
|
||||
try
|
||||
{
|
||||
return PublicationAuthorization.IsExpired;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal TimeSpan? DynamicCycleDeadlineRemaining()
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeSpan remaining = PublicationAuthorization.Remaining;
|
||||
return remaining > TimeSpan.Zero ? remaining : TimeSpan.Zero;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
internal EmPlanningPublicationDecision TryAuthorizePublication(
|
||||
CancellationToken coordinatorCallerCancellationToken, Action publish)
|
||||
{
|
||||
if (publish == null)
|
||||
throw new ArgumentNullException(nameof(publish));
|
||||
try
|
||||
{
|
||||
return PublicationAuthorization.TryPublish(CallerCancellationToken,
|
||||
coordinatorCallerCancellationToken, publish);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return EmPlanningPublicationDecision.DeadlineExpired;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The request owns this DTO contract; Task 2 adds its request-bound settings.
|
||||
/// <summary>
|
||||
/// 为请求契约保留的 <see cref="EmPlannerConfiguration"/> 部分声明;实际配置成员由其他同名分部提供。
|
||||
/// </summary>
|
||||
public sealed partial class EmPlannerConfiguration
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,8 +2,14 @@ using System;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 一次 EM 规划的不可变结果。只有成功状态才携带可发布的完整轨迹;其余状态仅提供诊断,不能作为部分执行轨迹消费。
|
||||
/// </summary>
|
||||
public sealed class EmPlanningResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建规划结果并强制成功状态与轨迹发布的一致性:<see cref="EmPlanningStatus.Success"/> 和 <see cref="EmPlanningStatus.SuccessWithFallback"/> 必须提供非 null 轨迹,其他状态必须提供 null 轨迹;null 失败原因规范化为空字符串。
|
||||
/// </summary>
|
||||
public EmPlanningResult(EmPlanningStatus status, EmTrajectory trajectory, string failureReason)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(EmPlanningStatus), status))
|
||||
@@ -20,9 +26,18 @@ public sealed class EmPlanningResult
|
||||
FailureReason = failureReason ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本次规划的最终状态;消费者必须先判断其是否为成功状态,再访问可发布轨迹。
|
||||
/// </summary>
|
||||
public EmPlanningStatus Status { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 仅在成功或成功降级状态下存在的完整可发布轨迹;失败、取消和无效输入结果始终为 null,消费者不得把失败结果当作部分轨迹执行。
|
||||
/// </summary>
|
||||
public EmTrajectory Trajectory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 面向诊断的失败或降级原因;null 输入已规范化为空字符串,不替代 <see cref="Status"/> 的机器可读状态。
|
||||
/// </summary>
|
||||
public string FailureReason { get; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 定义一次 EM 规划覆盖当前滚动窗口还是完整方向段。
|
||||
/// </summary>
|
||||
public enum EmPlanningScope
|
||||
{
|
||||
/// <summary>
|
||||
/// 仅覆盖有限的滚动规划窗口;段末之外保留给后续重规划。
|
||||
/// </summary>
|
||||
RollingHorizon,
|
||||
/// <summary>
|
||||
/// 覆盖选定方向段直至真实停车边界,并受完整段资源上限保护。
|
||||
/// </summary>
|
||||
FullDirectionSegment,
|
||||
}
|
||||
|
||||
@@ -1,26 +1,96 @@
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 EM 规划请求的最终处理状态;仅两个成功状态允许发布轨迹,其余状态要求消费者保留现有安全策略并读取诊断。
|
||||
/// </summary>
|
||||
public enum EmPlanningStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 所有规划和验证阶段成功,结果可按其生效时间发布。
|
||||
/// </summary>
|
||||
Success,
|
||||
/// <summary>
|
||||
/// 已发布经过验证的降级或回退轨迹;消费者仍可使用轨迹,并应记录诊断原因。
|
||||
/// </summary>
|
||||
SuccessWithFallback,
|
||||
/// <summary>
|
||||
/// 请求对象或其成员未满足服务输入要求。
|
||||
/// </summary>
|
||||
InvalidInput,
|
||||
/// <summary>
|
||||
/// 请求的车辆运动模型未被当前 EM 管线支持。
|
||||
/// </summary>
|
||||
UnsupportedMotionMode,
|
||||
/// <summary>
|
||||
/// 车辆状态采集时刻相对于请求时刻过旧。
|
||||
/// </summary>
|
||||
StaleVehicleState,
|
||||
/// <summary>
|
||||
/// 车辆状态的行驶方向与选定参考路径方向段不一致。
|
||||
/// </summary>
|
||||
StateDirectionMismatch,
|
||||
/// <summary>
|
||||
/// 参考路径结果、方向段或其状态不能用于规划。
|
||||
/// </summary>
|
||||
InvalidReferencePath,
|
||||
/// <summary>
|
||||
/// 无法将车辆状态投影到选定参考路径或方向段。
|
||||
/// </summary>
|
||||
ProjectionFailed,
|
||||
/// <summary>
|
||||
/// 地图和车辆约束下未能构造可行的行驶走廊。
|
||||
/// </summary>
|
||||
CorridorInfeasible,
|
||||
/// <summary>
|
||||
/// 横向优化未得到满足约束的候选解。
|
||||
/// </summary>
|
||||
LateralInfeasible,
|
||||
/// <summary>
|
||||
/// 纵向优化未得到满足时空与动力学约束的候选解。
|
||||
/// </summary>
|
||||
LongitudinalInfeasible,
|
||||
/// <summary>
|
||||
/// 当前速度、距离或限制不足以在所需边界前完成停车。
|
||||
/// </summary>
|
||||
StoppingDistanceInsufficient,
|
||||
/// <summary>
|
||||
/// 所需二次规划求解器不可用。
|
||||
/// </summary>
|
||||
SolverUnavailable,
|
||||
/// <summary>
|
||||
/// 求解在共享时间预算或迭代限制内未完成。
|
||||
/// </summary>
|
||||
SolverTimedOut,
|
||||
/// <summary>
|
||||
/// 调用方在可发布结果生成前取消了请求。
|
||||
/// </summary>
|
||||
Cancelled,
|
||||
/// <summary>
|
||||
/// 候选轨迹未通过最终世界空间、动力学或终端语义验证。
|
||||
/// </summary>
|
||||
ValidationFailed,
|
||||
/// <summary>
|
||||
/// 请求在处理期间被更新版本的规划工作替代。
|
||||
/// </summary>
|
||||
Superseded,
|
||||
/// <summary>
|
||||
/// 候选轨迹相对于当前状态未形成足够的有效进展。
|
||||
/// </summary>
|
||||
NoProgress,
|
||||
/// <summary>
|
||||
/// 轨迹末端位姿未满足要求的真实终端位姿。
|
||||
/// </summary>
|
||||
TerminalPoseMismatch,
|
||||
/// <summary>
|
||||
/// 完整方向段规划超过配置的时间、采样或求解资源上限。
|
||||
/// </summary>
|
||||
FullSegmentResourceLimitExceeded,
|
||||
/// <summary>
|
||||
/// 未被其他状态细分的规划失败。
|
||||
/// </summary>
|
||||
Failed,
|
||||
/// <summary>
|
||||
/// The outer planning cycle exhausted its shared bootstrap-to-publication deadline.
|
||||
/// </summary>
|
||||
CycleDeadlineExpired,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 表示已发布轨迹末端所对应的规划终端语义。
|
||||
/// </summary>
|
||||
public enum EmTerminalType
|
||||
{
|
||||
/// <summary>
|
||||
/// 因滚动窗口安全约束形成的停车终端,不代表方向段完成。
|
||||
/// </summary>
|
||||
RollingSafetyStop,
|
||||
/// <summary>
|
||||
/// 用于在方向改变前后执行换挡的终端。
|
||||
/// </summary>
|
||||
GearSwitch,
|
||||
/// <summary>
|
||||
/// 最终任务目标的终端。
|
||||
/// </summary>
|
||||
Goal,
|
||||
}
|
||||
|
||||
@@ -4,8 +4,14 @@ using System.Collections.ObjectModel;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 供成功规划结果发布的时间参数化轨迹;本类型只施加结构约束,世界空间复核由上游服务完成,点序列、元数据和终端语义在发布后保持不可变。
|
||||
/// </summary>
|
||||
public sealed class EmTrajectory
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建可发布的轨迹并复制点列表容器。元数据和每个点对象按引用保存、调用方仍拥有它们;传入列表可随后修改而不影响 <see cref="Points"/>,且 null 或空列表会抛出异常。
|
||||
/// </summary>
|
||||
public EmTrajectory(EmTrajectoryMetadata metadata, IReadOnlyList<EmTrajectoryPoint> points)
|
||||
{
|
||||
if (metadata == null)
|
||||
@@ -27,7 +33,13 @@ public sealed class EmTrajectory
|
||||
Points = new ReadOnlyCollection<EmTrajectoryPoint>(copy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹的不可变发布元数据引用;构造时必须非 null,未在本类中深拷贝。
|
||||
/// </summary>
|
||||
public EmTrajectoryMetadata Metadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 按调用方提供顺序保存的只读点列表;列表容器为构造时复制的快照,至少包含一个非 null 点,时间单调性由上游验证保证而非本构造器检查。
|
||||
/// </summary>
|
||||
public IReadOnlyList<EmTrajectoryPoint> Points { get; }
|
||||
}
|
||||
|
||||
@@ -3,8 +3,14 @@ using MultiWheelC.TrajectoryPlanning.CoarsePath;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹发布身份、来源版本、有效时间、方向段和终端边界的不可变元数据。
|
||||
/// </summary>
|
||||
public sealed class EmTrajectoryMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建轨迹的发布和来源元数据。验证标识、序列号、索引和枚举值;<paramref name="previousTrajectoryId"/> 可为 null 并会规范化为空字符串,两个 UTC 时刻的先后关系不在此处验证。
|
||||
/// </summary>
|
||||
public EmTrajectoryMetadata(
|
||||
string trajectoryId,
|
||||
DateTimeOffset generatedAtUtc,
|
||||
@@ -52,16 +58,63 @@ public sealed class EmTrajectoryMetadata
|
||||
PlanningScope = planningScope;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 非空白的本次发布轨迹标识,由消费者用于去重、替换和追踪。
|
||||
/// </summary>
|
||||
public string TrajectoryId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成此元数据的 UTC 时刻;值原样保存,构造器不与生效时刻比较。
|
||||
/// </summary>
|
||||
public DateTimeOffset GeneratedAtUtc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹计划开始生效的 UTC 时刻;执行消费者负责按其调度策略解释。
|
||||
/// </summary>
|
||||
public DateTimeOffset EffectiveAtUtc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 地图快照的非负版本标识;用于确认轨迹依赖的环境版本。
|
||||
/// </summary>
|
||||
public long MapSnapshotId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 非空白的输入参考路径标识;用于关联轨迹的几何来源。
|
||||
/// </summary>
|
||||
public string ReferencePathId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆状态的非负序列版本;用于判断轨迹是否基于当前状态快照。
|
||||
/// </summary>
|
||||
public long VehicleStateSequenceId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 前一轨迹的可选标识;null 输入已规范化为空字符串,空字符串表示没有可关联的前轨迹标识。
|
||||
/// </summary>
|
||||
public string PreviousTrajectoryId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 参考路径中此轨迹所属的非负方向段索引。
|
||||
/// </summary>
|
||||
public int SegmentIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属方向段的已验证行驶方向。
|
||||
/// </summary>
|
||||
public TravelDirection Direction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹最后一个终端的已验证业务类型。
|
||||
/// </summary>
|
||||
public EmTerminalType TerminalType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成轨迹时采用的已验证纵向终端速度语义。
|
||||
/// </summary>
|
||||
public EmLongitudinalMode LongitudinalMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成轨迹时采用的已验证规划覆盖范围。
|
||||
/// </summary>
|
||||
public EmPlanningScope PlanningScope { get; }
|
||||
}
|
||||
|
||||
@@ -3,8 +3,14 @@ using MultiWheelC.TrajectoryPlanning.CoarsePath;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 轨迹中的单个时间样本。世界位置单位 m、航向单位 rad、速度单位 m/s、曲率单位 1/m。
|
||||
/// </summary>
|
||||
public sealed class EmTrajectoryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建一个轨迹时间样本。所有浮点输入必须有限;时间、段内弧长和路径弧长必须非负,段索引和枚举值必须有效。派生的绝对速度、世界速度分量和偏航角速度由有符号纵向速度、航向和曲率计算。
|
||||
/// </summary>
|
||||
public EmTrajectoryPoint(
|
||||
double x,
|
||||
double y,
|
||||
@@ -62,23 +68,88 @@ public sealed class EmTrajectoryPoint
|
||||
LongitudinalJerk = longitudinalJerk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 世界坐标系 X 位置,单位 m。
|
||||
/// </summary>
|
||||
public double X { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 世界坐标系 Y 位置,单位 m。
|
||||
/// </summary>
|
||||
public double Y { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆在世界坐标系中的航向,单位 rad;构造器仅要求有限,不归一化角度。
|
||||
/// </summary>
|
||||
public double Yaw { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 沿车辆前向轴的有符号纵向速度,单位 m/s;符号表示前进或倒车。
|
||||
/// </summary>
|
||||
public double SignedLongitudinalVelocity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 有符号纵向速度的绝对值,单位 m/s。
|
||||
/// </summary>
|
||||
public double Speed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 由有符号纵向速度和航向导出的世界坐标 X 速度分量,单位 m/s。
|
||||
/// </summary>
|
||||
public double VelocityX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 由有符号纵向速度和航向导出的世界坐标 Y 速度分量,单位 m/s。
|
||||
/// </summary>
|
||||
public double VelocityY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 由有符号纵向速度乘车辆曲率导出的偏航角速度,单位 rad/s。
|
||||
/// </summary>
|
||||
public double YawRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 从本条轨迹开始执行起累计的非负时间,单位 s。
|
||||
/// </summary>
|
||||
public double TimeFromStart { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆路径曲率,单位 1/m;符号约定由上游几何计算定义。
|
||||
/// </summary>
|
||||
public double VehicleCurvature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 此点所属的非负方向段索引。
|
||||
/// </summary>
|
||||
public int SegmentIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 从该方向段开始沿参考路径累计的非负弧长,单位 m。
|
||||
/// </summary>
|
||||
public double SegmentLocalS { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 从整条参考路径开始累计的非负弧长,单位 m。
|
||||
/// </summary>
|
||||
public double PathS { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 此点的已验证行驶方向。
|
||||
/// </summary>
|
||||
public TravelDirection Direction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 此点的已验证边界语义;普通内部点使用 <see cref="EmBoundaryType.None"/>。
|
||||
/// </summary>
|
||||
public EmBoundaryType BoundaryType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 沿车辆前向轴的纵向加速度,单位 m/s²;仅供同程序集的轨迹验证和执行逻辑读取。
|
||||
/// </summary>
|
||||
internal double LongitudinalAcceleration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 沿车辆前向轴的纵向加加速度,单位 m/s³;仅供同程序集的轨迹验证和执行逻辑读取。
|
||||
/// </summary>
|
||||
internal double LongitudinalJerk { get; }
|
||||
}
|
||||
|
||||
@@ -3,8 +3,14 @@ using MultiWheelC.TrajectoryPlanning.CoarsePath;
|
||||
|
||||
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
||||
|
||||
/// <summary>
|
||||
/// 调用方一次性捕获的车辆运动状态;EMPlanner 只消费此快照,不直接读取定位、轮速或系统时钟。
|
||||
/// </summary>
|
||||
public sealed class VehicleMotionState
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存调用方捕获的车辆状态。构造器不校验 null、数值有限性、UTC 时效或序列号;服务在接受请求前负责验证,传入 <paramref name="pose"/> 的所有权仍归调用方。
|
||||
/// </summary>
|
||||
public VehicleMotionState(
|
||||
Pose2D pose,
|
||||
double signedLongitudinalSpeedMetersPerSecond,
|
||||
@@ -19,13 +25,28 @@ public sealed class VehicleMotionState
|
||||
SequenceId = sequenceId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 车辆世界位姿引用,其中位置单位为 m、航向单位为 rad;构造器允许 null,但服务要求有效位姿。
|
||||
/// </summary>
|
||||
public Pose2D Pose { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 沿车辆前向轴的有符号纵向速度,单位 m/s;正负方向约定由上游状态生产者负责,服务要求其为有限数。
|
||||
/// </summary>
|
||||
public double SignedLongitudinalSpeedMetersPerSecond { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 可选的沿车辆前向轴纵向加速度,单位 m/s²;null 表示采集方未提供该量,非 null 值必须由服务验证为有限数。
|
||||
/// </summary>
|
||||
public double? LongitudinalAccelerationMetersPerSecondSquared { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 采集此状态的 UTC 时刻;服务用它相对请求发起时刻判断状态是否过期。
|
||||
/// </summary>
|
||||
public DateTimeOffset CapturedAtUtc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 调用方提供的状态快照序列版本;服务要求其非负,成功轨迹将其写入元数据供消费者关联。
|
||||
/// </summary>
|
||||
public long SequenceId { get; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user