using System;
using System.Collections.Generic;
using ClumsyCore.Interfaces;
using ClumsyCore.Pilot;
using MDCSToolBox.Commons.Controllers;
using MultiWheelC.Control.Execution;
using MultiWheelC.StateEstimation;
using MultiWheelC.Trajectory;
using MyParking.Shared;
namespace MultiWheelC
{
///
/// 表示组合运动计划中由一种控制方式完整执行的单个动作段。
///
public abstract class MotionPlanSegment
{
}
///
/// 表示使用新版几何控制器跟踪一条连续二维轨迹的动作段。
///
public sealed class TrackMotionPlanSegment : MotionPlanSegment
{
public TrackMotionPlanSegment(Trajectory2D trajectory)
{
Trajectory = trajectory ??
throw new ArgumentNullException(nameof(trajectory));
}
public Trajectory2D Trajectory { get; }
///
/// 获取或设置本段轨迹独立的终点距离容差;为空时沿用轨迹动作默认值。
///
public double? FinishDistanceMeters { get; set; }
///
/// 获取或设置本段轨迹独立的停车速度容差;为空时沿用轨迹动作默认值。
///
public double? FinishSpeedMetersPerSecond { get; set; }
///
/// 获取或设置本段轨迹独立的终点航向容差;为空时沿用轨迹动作默认值。
///
public double? FinishHeadingToleranceRadians { get; set; }
}
///
/// 表示车辆停车后原地旋转到指定世界航向的动作段。
///
public sealed class RotateInPlaceMotionPlanSegment
: MotionPlanSegment
{
public RotateInPlaceMotionPlanSegment(
double targetYawRadians)
{
if (double.IsNaN(targetYawRadians) ||
double.IsInfinity(targetYawRadians))
{
throw new ArgumentOutOfRangeException(
nameof(targetYawRadians),
"原地自转目标航向必须是有限值。");
}
TargetYawRadians =
AngleMath.NormalizeRadians(targetYawRadians);
}
public double TargetYawRadians { get; }
}
///
/// 顺序执行连续轨迹和原地自转动作,并在动作段边界完成停车与控制器切换。
///
public sealed class MotionPlanExecutor : MovementDefinition
{
///
/// 获取或设置一次性提交并按顺序执行的组合运动计划。
///
public IReadOnlyList Segments;
///
/// 获取或设置所有动作段共享的车辆状态源;为空时使用Detour状态源。
///
public IVehicleStateProvider StateProvider;
///
/// 获取或设置创建每段轨迹动作后应用参数的回调。
///
public Action
ConfigureTrackingMovement;
///
/// 获取或设置创建每段原地自转动作后应用参数的回调。
///
public Action
ConfigureRotationMovement;
///
/// 获取或设置动作段开始前的通知,参数依次为索引和动作段。
///
public Action SegmentStarted;
///
/// 获取或设置轨迹段每个有效控制周期后的诊断通知。
///
public Action
TrackingCycleObserver;
///
/// 获取或设置自转段角速度命令通知,角速度单位为rad/s。
///
public Action RotationCommandObserver;
///
/// 按计划顺序执行各动作段,任一动作失败时停止后续动作。
///
public override IEnumerable Get()
{
if (Segments == null || Segments.Count == 0)
{
throw new InvalidOperationException(
"组合运动计划至少需要包含一个动作段。");
}
var stateProvider =
StateProvider ??
new DetourVehicleStateProvider();
for (var index = 0;
index < Segments.Count;
index++)
{
var segment = Segments[index] ??
throw new InvalidOperationException(
$"组合运动计划第{index}段为空。");
SegmentStarted?.Invoke(index, segment);
if (segment is TrackMotionPlanSegment track)
{
var movement =
new TrajectoryTrackingMovement
{
Trajectory = track.Trajectory,
StateProvider = stateProvider,
CycleObserver = controller =>
TrackingCycleObserver?.Invoke(
index,
controller)
};
ConfigureTrackingMovement?.Invoke(movement);
// 单段参数后应用,确保中间连接段可以覆盖组合动作的公共配置。
if (track.FinishDistanceMeters.HasValue)
{
movement.FinishDistanceMeters =
track.FinishDistanceMeters.Value;
}
if (track.FinishSpeedMetersPerSecond.HasValue)
{
movement.FinishSpeedMetersPerSecond =
track.FinishSpeedMetersPerSecond.Value;
}
if (track.FinishHeadingToleranceRadians.HasValue)
{
movement.FinishHeadingToleranceRadians =
track.FinishHeadingToleranceRadians.Value;
}
foreach (var keepRunning in movement.Get())
{
if (!keepRunning)
{
break;
}
yield return true;
}
continue;
}
if (segment is RotateInPlaceMotionPlanSegment rotate)
{
var config = PilotDefinition.Conf;
var movement =
new MultiWheelRotateInPlace
{
AngleTarget =
(float)AngleMath.RadiansToDegrees(
rotate.TargetYawRadians),
StateProvider = stateProvider,
PidparamsRead = () => new PIDParams
{
Kp = config.InPlaceRotateKp,
Ki = config.InPlaceRotateKi,
Kd = config.InPlaceRotateKd,
DeadZone =
config.InPlaceRotateArriveDeg,
SpeedAccPerSec =
config.InPlaceRotateAcc,
OutputUpperThreshold =
config.InPlaceRotateMaxSpeed,
MaxI = config.InPlaceRotateMaxI
},
MinimumAngularSpeedDegreesPerSecond =
config.InPlaceRotateMinimumSpeed,
WheelAlignmentToleranceDegrees =
config.InPlaceRotateWheelAlignDeg,
RotationTimeoutSeconds =
config.InPlaceRotateTimeoutSec,
CommandAngularSpeedObserver =
commandDegreesPerSecond =>
RotationCommandObserver?.Invoke(
index,
AngleMath.DegreesToRadians(
commandDegreesPerSecond))
};
ConfigureRotationMovement?.Invoke(movement);
foreach (var keepRunning in movement.Get())
{
if (!keepRunning)
{
break;
}
yield return true;
}
continue;
}
throw new NotSupportedException(
$"组合运动计划不支持动作段类型:{segment.GetType().FullName}。");
}
// 所有子动作均已完成后,才向外层DriveTask发送组合计划结束信号。
yield return false;
}
}
}