using System;
using MultiWheelC.Control.Abstractions;
using MultiWheelC.Control.Common;
namespace MultiWheelC.Control.Longitudinal
{
///
/// 将轨迹参考速度前馈与通用PID速度反馈组合为有符号底盘命令速度。
///
public sealed class PidLongitudinalController
: ILongitudinalController
{
private const double ReferenceStopDeadbandMetersPerSecond =
1e-6;
private readonly PidController _feedbackPid;
///
/// 创建具有积分抗饱和和命令速度限幅的纵向速度外环。
///
public PidLongitudinalController(
double proportionalGain,
double integralGainPerSecond,
double derivativeGainSeconds,
double maximumIntegralCorrectionMetersPerSecond,
double maximumCommandSpeedMetersPerSecond,
double speedErrorDeadbandMetersPerSecond = 0.025)
{
EnsureFinitePositive(
maximumCommandSpeedMetersPerSecond,
nameof(maximumCommandSpeedMetersPerSecond));
EnsureFiniteNonNegative(
speedErrorDeadbandMetersPerSecond,
nameof(speedErrorDeadbandMetersPerSecond));
_feedbackPid = new PidController(
proportionalGain,
integralGainPerSecond,
derivativeGainSeconds,
maximumIntegralCorrectionMetersPerSecond,
derivativeOnMeasurement: true);
MaximumCommandSpeedMetersPerSecond =
maximumCommandSpeedMetersPerSecond;
SpeedErrorDeadbandMetersPerSecond =
speedErrorDeadbandMetersPerSecond;
}
///
/// 获取负责计算速度误差修正量的通用PID控制器。
///
public PidController FeedbackPid => _feedbackPid;
///
/// 获取底盘命令速度的最大绝对值,单位为m/s。
///
public double MaximumCommandSpeedMetersPerSecond { get; }
///
/// 获取不触发纵向PID修正的速度误差死区,单位为m/s。
///
public double SpeedErrorDeadbandMetersPerSecond { get; }
///
/// 获取最近一次有效控制周期的参考速度减实际速度,单位为m/s。
///
public double LastSpeedErrorMetersPerSecond =>
_feedbackPid.LastError;
///
/// 获取最近一次比例项产生的速度修正,单位为m/s。
///
public double LastProportionalCorrectionMetersPerSecond =>
_feedbackPid.LastProportionalOutput;
///
/// 获取最近一次积分项产生的速度修正,单位为m/s。
///
public double LastIntegralCorrectionMetersPerSecond =>
_feedbackPid.LastIntegralOutput;
///
/// 获取最近一次微分项产生的速度修正,单位为m/s。
///
public double LastDerivativeCorrectionMetersPerSecond =>
_feedbackPid.LastDerivativeOutput;
///
/// 根据轨迹参考速度和Detour实际纵向速度计算底盘命令速度。
///
public double ComputeSpeedMetersPerSecond(
PathTrackingContext context)
{
var referenceSpeedMetersPerSecond =
context.ReferenceSpeedMetersPerSecond;
// 轨迹明确要求停车时直接输出零,防止速度反馈使车辆在终点反向纠偏。
if (Math.Abs(referenceSpeedMetersPerSecond) <=
ReferenceStopDeadbandMetersPerSecond)
{
Reset();
return 0.0;
}
// 定位速度尚不可用时只透传参考速度,不使用无效反馈更新PID状态。
if (!context.HasValidVelocityEstimate)
{
Reset();
return LimitReferenceSpeed(
referenceSpeedMetersPerSecond);
}
var speedErrorMetersPerSecond =
referenceSpeedMetersPerSecond -
context.ActualLongitudinalSpeedMetersPerSecond;
// Detour差分速度在参考速度附近会有小幅波动;死区内只使用速度前馈,
// 同时清除PID历史,避免噪声持续积累后产生突发修正。
if (Math.Abs(speedErrorMetersPerSecond) <=
SpeedErrorDeadbandMetersPerSecond)
{
Reset();
return LimitReferenceSpeed(
referenceSpeedMetersPerSecond);
}
GetCorrectionOutputRange(
referenceSpeedMetersPerSecond,
out var minimumCorrectionMetersPerSecond,
out var maximumCorrectionMetersPerSecond);
var correctionMetersPerSecond =
_feedbackPid.Update(
referenceSpeedMetersPerSecond,
context
.ActualLongitudinalSpeedMetersPerSecond,
context.DeltaTimeSeconds,
minimumCorrectionMetersPerSecond,
maximumCorrectionMetersPerSecond);
return referenceSpeedMetersPerSecond +
correctionMetersPerSecond;
}
///
/// 清除纵向速度外环的积分、历史测量值和诊断输出。
///
public void Reset()
{
_feedbackPid.Reset();
}
///
/// 根据参考行驶方向计算PID修正量允许使用的动态输出范围。
///
private void GetCorrectionOutputRange(
double referenceSpeedMetersPerSecond,
out double minimumCorrectionMetersPerSecond,
out double maximumCorrectionMetersPerSecond)
{
if (referenceSpeedMetersPerSecond > 0.0)
{
minimumCorrectionMetersPerSecond =
-referenceSpeedMetersPerSecond;
maximumCorrectionMetersPerSecond =
MaximumCommandSpeedMetersPerSecond -
referenceSpeedMetersPerSecond;
return;
}
minimumCorrectionMetersPerSecond =
-MaximumCommandSpeedMetersPerSecond -
referenceSpeedMetersPerSecond;
maximumCorrectionMetersPerSecond =
-referenceSpeedMetersPerSecond;
}
///
/// 在没有有效速度反馈时限制参考速度的绝对值。
///
private double LimitReferenceSpeed(
double referenceSpeedMetersPerSecond)
{
return Math.Max(
-MaximumCommandSpeedMetersPerSecond,
Math.Min(
MaximumCommandSpeedMetersPerSecond,
referenceSpeedMetersPerSecond));
}
///
/// 检查最大命令速度是否为正有限值。
///
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,
"纵向控制器速度误差死区必须是非负有限值。");
}
}
}
}