using System; using System.Diagnostics; using MultiWheelC.Control.Abstractions; using MultiWheelC.Control.Allocation; using MultiWheelC.StateEstimation; using MultiWheelC.Trajectory; using MyParking.Shared; namespace MultiWheelC.Control.Execution { // 单周期轨迹控制结果。 public enum ParkingControlCycleResult { Inactive = 0, CommandSent = 1, Completed = 2, StateUnavailable = 3, Faulted = 4 } // 单周期各阶段耗时及定位帧更新状态,用于区分控制计算与底层通信延迟。 public readonly struct ParkingControlCycleTiming { public ParkingControlCycleTiming( long cycleIndex, double cycleIntervalMilliseconds, double stateReadMilliseconds, double projectionMilliseconds, double controllerComputeMilliseconds, double commandSendMilliseconds, double totalCycleMilliseconds, bool hasStateTimestamp, double stateTimestampSeconds, bool stateTimestampChanged, ParkingControlCycleResult result) { CycleIndex = cycleIndex; CycleIntervalMilliseconds = cycleIntervalMilliseconds; StateReadMilliseconds = stateReadMilliseconds; ProjectionMilliseconds = projectionMilliseconds; ControllerComputeMilliseconds = controllerComputeMilliseconds; CommandSendMilliseconds = commandSendMilliseconds; TotalCycleMilliseconds = totalCycleMilliseconds; HasStateTimestamp = hasStateTimestamp; StateTimestampSeconds = stateTimestampSeconds; StateTimestampChanged = stateTimestampChanged; Result = result; } public long CycleIndex { get; } public double CycleIntervalMilliseconds { get; } public double StateReadMilliseconds { get; } public double ProjectionMilliseconds { get; } public double ControllerComputeMilliseconds { get; } public double CommandSendMilliseconds { get; } public double TotalCycleMilliseconds { get; } public bool HasStateTimestamp { get; } public double StateTimestampSeconds { get; } public bool StateTimestampChanged { get; } public ParkingControlCycleResult Result { get; } } // 负责单车状态读取、公共轨迹计算和真实底盘命令发送。 public sealed class ParkingGeometricController { private readonly IVehicleStateProvider _stateProvider; private readonly GcpCommandExecutor _commandExecutor; private readonly PathTrackingCore _trackingCore; private long _cycleIndex; private bool _hasPreviousStateTimestamp; private double _previousStateTimestampSeconds; // 创建具有终点判定、轨迹偏离保护和曲率前馈预瞄的单车轨迹控制器。 public ParkingGeometricController( IVehicleStateProvider stateProvider, ILateralController lateralController, ILongitudinalController longitudinalController, GcpCommandAllocator gcpAllocator, GcpCommandExecutor commandExecutor, double finishDistanceMeters = 0.04, double finishSpeedMetersPerSecond = 0.02, double finishHeadingToleranceRadians = 3.0 * Math.PI / 180.0, double maximumDistanceToTrajectoryMeters = 0.30, double terminalBrakingPreviewMeters = 0.02, double terminalApproachDistanceMeters = 0.10, double terminalApproachGainPerSecond = 0.8, double maximumTerminalApproachSpeedMetersPerSecond = 0.05, double curvaturePreviewSeconds = 0.20, double maximumCurvaturePreviewMeters = 0.12, double motionDirectionInBodyRadians = 0.0) { _stateProvider = stateProvider ?? throw new ArgumentNullException( nameof(stateProvider)); _commandExecutor = commandExecutor ?? throw new ArgumentNullException( nameof(commandExecutor)); _trackingCore = new PathTrackingCore( lateralController, longitudinalController, gcpAllocator, finishDistanceMeters, finishSpeedMetersPerSecond, finishHeadingToleranceRadians, maximumDistanceToTrajectoryMeters, terminalBrakingPreviewMeters, terminalApproachDistanceMeters, terminalApproachGainPerSecond, maximumTerminalApproachSpeedMetersPerSecond, curvaturePreviewSeconds, maximumCurvaturePreviewMeters, motionDirectionInBodyRadians); } // 以下控制参数由公共轨迹核心统一持有。 public double FinishDistanceMeters => _trackingCore.FinishDistanceMeters; public double FinishSpeedMetersPerSecond => _trackingCore.FinishSpeedMetersPerSecond; public double FinishHeadingToleranceRadians => _trackingCore.FinishHeadingToleranceRadians; public double MaximumDistanceToTrajectoryMeters => _trackingCore.MaximumDistanceToTrajectoryMeters; public double TerminalBrakingPreviewMeters => _trackingCore.TerminalBrakingPreviewMeters; public double TerminalApproachDistanceMeters => _trackingCore.TerminalApproachDistanceMeters; public double TerminalApproachGainPerSecond => _trackingCore.TerminalApproachGainPerSecond; public double MaximumTerminalApproachSpeedMetersPerSecond => _trackingCore.MaximumTerminalApproachSpeedMetersPerSecond; public double CurvaturePreviewSeconds => _trackingCore.CurvaturePreviewSeconds; public double MaximumCurvaturePreviewMeters => _trackingCore.MaximumCurvaturePreviewMeters; public bool IsActive => _trackingCore.IsActive; public bool IsCompleted => _trackingCore.IsCompleted; public string LastFailureReason => _trackingCore.LastFailureReason; public Exception LastException => _trackingCore.LastException; // 最近一次有效车辆状态仍由单车外层保存。 public VehicleState? LastVehicleState { get; private set; } public TrajectoryProjection? LastProjection => _trackingCore.LastProjection; public GcpMotionCommand? LastRequestedCommand => _trackingCore.LastRequestedCommand; // 经过GCP角速度限制后实际发送给底盘的最近一次命令。 public GcpMotionCommand? LastCommand { get; private set; } public double? LastControlReferenceSpeedMetersPerSecond => _trackingCore.LastControlReferenceSpeedMetersPerSecond; public double? LastCurvaturePreviewDistanceMeters => _trackingCore.LastCurvaturePreviewDistanceMeters; public double? LastFeedforwardCurvaturePerMeter => _trackingCore.LastFeedforwardCurvaturePerMeter; public ParkingControlCycleTiming? LastCycleTiming { get; private set; } // 先停车,再从轨迹起点重置公共控制核心和单车执行诊断。 public void Start(Trajectory2D trajectory) { _commandExecutor.Stop(); _trackingCore.Start(trajectory); ClearExecutionDiagnostics(); } // 读取车辆状态、调用公共核心并发送一次真实底盘命令。 public ParkingControlCycleResult ExecuteCycle( double deltaTimeSeconds) { NumericGuard.EnsureFinitePositive( deltaTimeSeconds, nameof(deltaTimeSeconds)); if (!_trackingCore.IsActive) { return ParkingControlCycleResult.Inactive; } var cycleIndex = ++_cycleIndex; var cycleStartTimestamp = Stopwatch.GetTimestamp(); var stateReadMilliseconds = 0.0; var projectionMilliseconds = 0.0; var controllerComputeMilliseconds = 0.0; var commandSendMilliseconds = 0.0; var hasStateTimestamp = false; var stateTimestampSeconds = 0.0; var stateTimestampChanged = false; var cycleResult = ParkingControlCycleResult.Faulted; try { var stateReadStartTimestamp = Stopwatch.GetTimestamp(); bool stateAvailable; VehicleState vehicleState; try { stateAvailable = _stateProvider.TryGetState( out vehicleState); } finally { stateReadMilliseconds = GetElapsedMilliseconds( stateReadStartTimestamp); } if (!stateAvailable) { var stopStartTimestamp = Stopwatch.GetTimestamp(); try { StopForUnavailableState(); } finally { commandSendMilliseconds = GetElapsedMilliseconds( stopStartTimestamp); } cycleResult = ParkingControlCycleResult .StateUnavailable; return cycleResult; } LastVehicleState = vehicleState; hasStateTimestamp = true; stateTimestampSeconds = vehicleState.SampleTimestampSeconds; stateTimestampChanged = !_hasPreviousStateTimestamp || stateTimestampSeconds != _previousStateTimestampSeconds; _previousStateTimestampSeconds = stateTimestampSeconds; _hasPreviousStateTimestamp = true; var output = _trackingCore.Compute( vehicleState.PoseInWorld, vehicleState.TwistInBody, vehicleState.HasValidVelocityEstimate, deltaTimeSeconds); projectionMilliseconds = output.ProjectionMilliseconds; controllerComputeMilliseconds = output.ControllerComputeMilliseconds; if (output.Result == PathTrackingCycleResult.Completed) { commandSendMilliseconds = StopForCompletedTrajectory(); cycleResult = ParkingControlCycleResult.Completed; return cycleResult; } if (output.Result == PathTrackingCycleResult.Faulted) { commandSendMilliseconds = StopForTrackingFault(); cycleResult = ParkingControlCycleResult.Faulted; return cycleResult; } if (output.Result != PathTrackingCycleResult.CommandGenerated || !output.Command.HasValue) { cycleResult = ParkingControlCycleResult.Inactive; return cycleResult; } var commandSendStartTimestamp = Stopwatch.GetTimestamp(); bool commandSucceeded; try { commandSucceeded = _commandExecutor.Execute( output.Command.Value, deltaTimeSeconds); } finally { commandSendMilliseconds = GetElapsedMilliseconds( commandSendStartTimestamp); } if (!commandSucceeded) { cycleResult = EnterFault( string.IsNullOrWhiteSpace( _commandExecutor.LastFailureReason) ? "GCP底盘命令执行失败。" : _commandExecutor.LastFailureReason); return cycleResult; } LastCommand = _commandExecutor.LastSentCommand; cycleResult = ParkingControlCycleResult.CommandSent; return cycleResult; } catch (Exception exception) { cycleResult = EnterFault( "停车机器人轨迹控制周期异常:" + exception.Message, exception); return cycleResult; } finally { LastCycleTiming = new ParkingControlCycleTiming( cycleIndex, deltaTimeSeconds * 1000.0, stateReadMilliseconds, projectionMilliseconds, controllerComputeMilliseconds, commandSendMilliseconds, GetElapsedMilliseconds( cycleStartTimestamp), hasStateTimestamp, stateTimestampSeconds, stateTimestampChanged, cycleResult); } } // 主动取消当前轨迹、立即停车并清除全部控制状态。 public void Cancel() { _commandExecutor.Stop(); _trackingCore.Cancel(); ClearExecutionDiagnostics(); } // 状态不可用时停车并重置反馈历史,同时保留轨迹等待恢复。 private void StopForUnavailableState() { _commandExecutor.Stop(); _trackingCore.PauseForUnavailableState( "当前无法获得有效车辆状态,底盘已停车并等待定位恢复。"); LastCommand = null; } // 公共核心完成轨迹后发送停车,并保留零命令供实验记录。 private double StopForCompletedTrajectory() { var startTimestamp = Stopwatch.GetTimestamp(); _commandExecutor.Stop(); LastCommand = new GcpMotionCommand( 0.0, 0.0, 0.0); return GetElapsedMilliseconds(startTimestamp); } // 公共核心故障后只负责真实底盘停车,不覆盖核心保存的失败原因。 private double StopForTrackingFault() { var startTimestamp = Stopwatch.GetTimestamp(); _commandExecutor.Stop(); LastCommand = null; return GetElapsedMilliseconds(startTimestamp); } // 将底盘执行或外层异常同步到公共核心,并立即停车。 private ParkingControlCycleResult EnterFault( string reason, Exception exception = null) { _commandExecutor.Stop(); _trackingCore.Fail(reason, exception); LastCommand = null; return ParkingControlCycleResult.Faulted; } // 清除只属于单车状态读取、发送和周期计时的诊断。 private void ClearExecutionDiagnostics() { _cycleIndex = 0; _hasPreviousStateTimestamp = false; _previousStateTimestampSeconds = 0.0; LastVehicleState = null; LastCommand = null; LastCycleTiming = null; } private static double GetElapsedMilliseconds( long startTimestamp) { return (Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / Stopwatch.Frequency; } } }