using System; 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 } /// /// 组织状态读取、轨迹投影、横纵向控制、GCP分配和底盘命令执行。 /// public sealed class ParkingGeometricController { private const double ZeroReferenceSpeedToleranceMetersPerSecond = 1e-6; private const double StartupRegionMeters = 0.02; private const double StartupPreviewDistanceMeters = 0.05; private const double MaximumStartupSpeedMetersPerSecond = 0.08; private readonly IVehicleStateProvider _stateProvider; private readonly ILateralController _lateralController; private readonly ILongitudinalController _longitudinalController; private readonly AckermannGcpAllocator _gcpAllocator; private readonly GcpCommandExecutor _commandExecutor; private Trajectory2D _trajectory; /// /// 创建具有终点判定和轨迹偏离保护的单车轨迹控制器。 /// public ParkingGeometricController( IVehicleStateProvider stateProvider, ILateralController lateralController, ILongitudinalController longitudinalController, AckermannGcpAllocator gcpAllocator, GcpCommandExecutor commandExecutor, double finishDistanceMeters = 0.03, double finishSpeedMetersPerSecond = 0.02, double finishHeadingToleranceRadians = 3.0 * Math.PI / 180.0, double maximumDistanceToTrajectoryMeters = 0.50) { _stateProvider = stateProvider ?? throw new ArgumentNullException( nameof(stateProvider)); _lateralController = lateralController ?? throw new ArgumentNullException( nameof(lateralController)); _longitudinalController = longitudinalController ?? throw new ArgumentNullException( nameof(longitudinalController)); _gcpAllocator = gcpAllocator ?? throw new ArgumentNullException( nameof(gcpAllocator)); _commandExecutor = commandExecutor ?? throw new ArgumentNullException( nameof(commandExecutor)); EnsureFinitePositive( finishDistanceMeters, nameof(finishDistanceMeters)); EnsureFiniteNonNegative( finishSpeedMetersPerSecond, nameof(finishSpeedMetersPerSecond)); EnsureFinitePositive( finishHeadingToleranceRadians, nameof(finishHeadingToleranceRadians)); EnsureFinitePositive( maximumDistanceToTrajectoryMeters, nameof(maximumDistanceToTrajectoryMeters)); FinishDistanceMeters = finishDistanceMeters; FinishSpeedMetersPerSecond = finishSpeedMetersPerSecond; FinishHeadingToleranceRadians = finishHeadingToleranceRadians; MaximumDistanceToTrajectoryMeters = maximumDistanceToTrajectoryMeters; } /// /// 获取终点位置和剩余弧长允许的误差,单位为m。 /// public double FinishDistanceMeters { get; } /// /// 获取判定轨迹执行完成时允许的最大实际线速度,单位为m/s。 /// public double FinishSpeedMetersPerSecond { get; } /// /// 获取判定轨迹完成时允许的最大终点航向误差,单位为rad。 /// public double FinishHeadingToleranceRadians { get; } /// /// 获取允许车辆偏离参考轨迹的最大距离,单位为m。 /// public double MaximumDistanceToTrajectoryMeters { get; } /// /// 获取控制器当前是否持有并正在执行一条轨迹。 /// public bool IsActive { get; private set; } /// /// 获取最近一次轨迹是否已经满足终点完成条件。 /// public bool IsCompleted { get; private set; } /// /// 获取最近一次控制失败原因,正常时为空字符串。 /// public string LastFailureReason { get; private set; } = string.Empty; /// /// 获取最近一次控制异常,正常时为空。 /// public Exception LastException { get; private set; } /// /// 获取最近一次有效车辆状态。 /// public VehicleState? LastVehicleState { get; private set; } /// /// 获取最近一次车体中心到参考轨迹的投影结果。 /// public TrajectoryProjection? LastProjection { get; private set; } /// /// 获取最近一次发送或准备发送的GCP运动命令。 /// public GcpMotionCommand? LastCommand { get; private set; } /// /// 获取最近控制周期实际交给纵向控制器的参考速度,单位为m/s。 /// public double? LastReferenceSpeedMetersPerSecond { get; private set; } /// /// 停止当前底盘并从起点开始执行指定二维轨迹。 /// public void Start(Trajectory2D trajectory) { if (trajectory == null) { throw new ArgumentNullException( nameof(trajectory)); } StopAndResetControllers(); _trajectory = trajectory; IsActive = true; IsCompleted = false; ClearDiagnostics(); } /// /// 读取本周期车辆状态并执行一次完整的轨迹跟踪控制计算。 /// public ParkingControlCycleResult ExecuteCycle( double deltaTimeSeconds) { EnsureFinitePositive( deltaTimeSeconds, nameof(deltaTimeSeconds)); if (!IsActive || _trajectory == null) { return ParkingControlCycleResult.Inactive; } try { if (!_stateProvider.TryGetState( out var vehicleState)) { StopForUnavailableState(); return ParkingControlCycleResult .StateUnavailable; } LastVehicleState = vehicleState; var projection = TrajectoryProjector.Project( _trajectory, vehicleState.PoseInWorld); LastProjection = projection; if (projection.DistanceToTrajectoryMeters > MaximumDistanceToTrajectoryMeters) { return EnterFault( "车辆距离参考轨迹" + $"{projection.DistanceToTrajectoryMeters:F3}m," + "超过允许值" + $"{MaximumDistanceToTrajectoryMeters:F3}m。"); } if (HasReachedEnd( vehicleState, projection)) { CompleteTrajectory(); return ParkingControlCycleResult.Completed; } if (HasStoppedAtUnsatisfiedTerminal( vehicleState, projection, out var terminalFailureReason)) { return EnterFault( terminalFailureReason); } var referenceSpeedMetersPerSecond = ResolveReferenceSpeedForControl( projection); LastReferenceSpeedMetersPerSecond = referenceSpeedMetersPerSecond; var context = new PathTrackingContext( vehicleState, projection, referenceSpeedMetersPerSecond, deltaTimeSeconds); var lateralCommand = _lateralController.Compute(context); var commandSpeedMetersPerSecond = _longitudinalController .ComputeSpeedMetersPerSecond(context); var gcpCommand = _gcpAllocator.Allocate( commandSpeedMetersPerSecond, lateralCommand); if (!_commandExecutor.Execute( gcpCommand, deltaTimeSeconds)) { return EnterFault( string.IsNullOrWhiteSpace( _commandExecutor.LastFailureReason) ? "GCP底盘命令执行失败。" : _commandExecutor.LastFailureReason); } LastCommand = _commandExecutor.LastSentCommand; LastFailureReason = string.Empty; LastException = null; return ParkingControlCycleResult.CommandSent; } catch (Exception exception) { return EnterFault( "停车机器人轨迹控制周期异常:" + exception.Message, exception); } } /// /// 主动取消当前轨迹、立即停车并清除全部控制器状态。 /// public void Cancel() { StopAndResetControllers(); _trajectory = null; IsActive = false; IsCompleted = false; ClearDiagnostics(); } /// /// 在轨迹起点零速固定点处读取前方速度,并限制为低速起步命令。 /// private double ResolveReferenceSpeedForControl( TrajectoryProjection projection) { var currentReferenceSpeed = projection.ReferencePoint .ReferenceSpeedMetersPerSecond; var requiresStartupRelease = projection.ArcLengthMeters <= StartupRegionMeters && projection.RemainingDistanceMeters > FinishDistanceMeters && Math.Abs(currentReferenceSpeed) <= ZeroReferenceSpeedToleranceMetersPerSecond; if (!requiresStartupRelease) { return currentReferenceSpeed; } var previewArcLengthMeters = Math.Min( _trajectory.TotalLengthMeters, projection.ArcLengthMeters + StartupPreviewDistanceMeters); var previewReferenceSpeed = _trajectory .SampleAtArcLength( previewArcLengthMeters) .ReferenceSpeedMetersPerSecond; if (Math.Abs(previewReferenceSpeed) <= ZeroReferenceSpeedToleranceMetersPerSecond) { return 0.0; } return Math.Sign(previewReferenceSpeed) * Math.Min( Math.Abs(previewReferenceSpeed), MaximumStartupSpeedMetersPerSecond); } /// /// 根据终点距离、剩余弧长和实际线速度判断轨迹是否完成。 /// private bool HasReachedEnd( VehicleState vehicleState, TrajectoryProjection projection) { if (!vehicleState.HasValidVelocityEstimate) { return false; } return projection.RemainingDistanceMeters <= FinishDistanceMeters && CalculateDistanceToEndMeters( vehicleState) <= FinishDistanceMeters && CalculateHeadingErrorToEndRadians( vehicleState) <= FinishHeadingToleranceRadians && CalculateActualLinearSpeedMetersPerSecond( vehicleState) <= FinishSpeedMetersPerSecond; } /// /// 检查车辆是否已在终点零速参考处停稳但最终位置或航向仍不合格。 /// private bool HasStoppedAtUnsatisfiedTerminal( VehicleState vehicleState, TrajectoryProjection projection, out string failureReason) { failureReason = string.Empty; var isTerminalZeroSpeedReference = projection.RemainingDistanceMeters <= FinishDistanceMeters && Math.Abs( projection.ReferencePoint .ReferenceSpeedMetersPerSecond) <= ZeroReferenceSpeedToleranceMetersPerSecond; if (!isTerminalZeroSpeedReference || !vehicleState.HasValidVelocityEstimate || CalculateActualLinearSpeedMetersPerSecond( vehicleState) > FinishSpeedMetersPerSecond) { return false; } var positionErrorMeters = CalculateDistanceToEndMeters( vehicleState); var headingErrorRadians = CalculateHeadingErrorToEndRadians( vehicleState); failureReason = "车辆已在终点零速参考处停稳,但终点精度不满足要求:" + $"位置误差={positionErrorMeters:F3}m," + "航向误差=" + $"{AngleMath.RadiansToDegrees(headingErrorRadians):F2}°。"; return true; } /// /// 计算实际车体中心到轨迹终点的欧氏距离,单位为m。 /// private double CalculateDistanceToEndMeters( VehicleState vehicleState) { var endPoint = _trajectory.EndPoint.PoseInWorld; var deltaX = vehicleState.PoseInWorld.XMeters - endPoint.XMeters; var deltaY = vehicleState.PoseInWorld.YMeters - endPoint.YMeters; return Math.Sqrt( deltaX * deltaX + deltaY * deltaY); } /// /// 计算实际车体航向到轨迹终点航向的最短角度误差绝对值,单位为rad。 /// private double CalculateHeadingErrorToEndRadians( VehicleState vehicleState) { return Math.Abs( AngleMath.ShortestDifferenceRadians( _trajectory.EndPoint .PoseInWorld.YawRadians, vehicleState .PoseInWorld.YawRadians)); } /// /// 计算车体坐标系实际线速度的合速度绝对值,单位为m/s。 /// private static double CalculateActualLinearSpeedMetersPerSecond( VehicleState vehicleState) { return Math.Sqrt( vehicleState.TwistInBody.VxMetersPerSecond * vehicleState.TwistInBody.VxMetersPerSecond + vehicleState.TwistInBody.VyMetersPerSecond * vehicleState.TwistInBody.VyMetersPerSecond); } /// /// 在状态暂不可用时停车并重置反馈控制器,同时保留轨迹等待下一周期恢复。 /// private void StopForUnavailableState() { _commandExecutor.Stop(); _lateralController.Reset(); _longitudinalController.Reset(); LastCommand = null; LastFailureReason = "当前无法获得有效车辆状态,底盘已停车并等待定位恢复。"; LastException = null; } /// /// 完成当前轨迹并停车,但保留最后状态和投影供实验记录读取。 /// private void CompleteTrajectory() { StopAndResetControllers(); IsActive = false; IsCompleted = true; LastCommand = new GcpMotionCommand( 0.0, 0.0, 0.0); LastFailureReason = string.Empty; LastException = null; } /// /// 发生不可继续的控制故障时停车、退出活动状态并保存诊断信息。 /// private ParkingControlCycleResult EnterFault( string reason, Exception exception = null) { StopAndResetControllers(); IsActive = false; IsCompleted = false; LastCommand = null; LastFailureReason = reason; LastException = exception; return ParkingControlCycleResult.Faulted; } /// /// 立即停止底盘并清除横向和纵向控制器的跨周期状态。 /// private void StopAndResetControllers() { _commandExecutor.Stop(); _lateralController.Reset(); _longitudinalController.Reset(); } /// /// 清除上一条轨迹留下的状态、命令和故障诊断信息。 /// private void ClearDiagnostics() { LastVehicleState = null; LastProjection = null; LastCommand = null; LastReferenceSpeedMetersPerSecond = null; LastFailureReason = string.Empty; LastException = null; } /// /// 检查控制参数是否为正有限值。 /// private static void EnsureFinitePositive( double value, string parameterName) { if (double.IsNaN(value) || double.IsInfinity(value) || value <= 0.0) { throw new ArgumentOutOfRangeException( parameterName, "轨迹控制器距离和周期参数必须是正有限值。"); } } /// /// 检查控制参数是否为非负有限值。 /// private static void EnsureFiniteNonNegative( double value, string parameterName) { if (double.IsNaN(value) || double.IsInfinity(value) || value < 0.0) { throw new ArgumentOutOfRangeException( parameterName, "轨迹控制器速度参数必须是非负有限值。"); } } } }