增加GCP运动学并完善车体平面速度反馈分析

This commit is contained in:
2026-08-14 17:45:17 +08:00
parent 9fe8901c4c
commit d8de901a80
21 changed files with 445 additions and 38 deletions
@@ -6,7 +6,7 @@ using System.Diagnostics;
namespace MultiWheelC.StateEstimation
{
/// <summary>
/// 保留外部状态源的Detour位姿,并以舵轮电机反馈解算的车体纵向速度替换Detour差分纵向速度。
/// 保留外部状态源的Detour位姿,并以舵轮电机反馈解算的车体平面速度替换Detour差分线速度。
/// </summary>
public sealed class WheelFeedbackVehicleStateProvider
: IVehicleStateProvider
@@ -19,6 +19,7 @@ namespace MultiWheelC.StateEstimation
private readonly IVehicleStateProvider _poseProvider;
private readonly MultiWheelChassis _chassis;
private readonly FirstOrderLowPassFilter _longitudinalSpeedFilter;
private readonly FirstOrderLowPassFilter _lateralSpeedFilter;
private bool _hasPreviousTimestamp;
private double _previousTimestampSeconds;
@@ -27,10 +28,12 @@ namespace MultiWheelC.StateEstimation
private bool _latestDetourVelocityValid;
private double _latestRawWheelBodyVxMetersPerSecond;
private double _latestFilteredWheelBodyVxMetersPerSecond;
private double _latestRawWheelBodyVyMetersPerSecond;
private double _latestFilteredWheelBodyVyMetersPerSecond;
private bool _latestWheelVelocityValid;
/// <summary>
/// 创建使用默认0.10s低通时间常数的电机反馈纵向速度状态源。
/// 创建使用默认0.10s低通时间常数的电机反馈平面速度状态源。
/// </summary>
public WheelFeedbackVehicleStateProvider(
IVehicleStateProvider poseProvider,
@@ -43,7 +46,7 @@ namespace MultiWheelC.StateEstimation
}
/// <summary>
/// 创建使用指定低通时间常数的电机反馈纵向速度状态源。
/// 创建使用指定低通时间常数的电机反馈平面速度状态源。
/// </summary>
public WheelFeedbackVehicleStateProvider(
IVehicleStateProvider poseProvider,
@@ -59,6 +62,9 @@ namespace MultiWheelC.StateEstimation
_longitudinalSpeedFilter =
new FirstOrderLowPassFilter(
velocityFilterTimeConstantSeconds);
_lateralSpeedFilter =
new FirstOrderLowPassFilter(
velocityFilterTimeConstantSeconds);
}
/// <summary>
@@ -87,40 +93,50 @@ namespace MultiWheelC.StateEstimation
{
var actualCarSpeed =
_chassis.GetCarSpeed(true);
var rawLongitudinalSpeedMetersPerSecond =
var rawBodyVxMetersPerSecond =
(double)actualCarSpeed.Vx;
var rawBodyVyMetersPerSecond =
(double)actualCarSpeed.Vy;
NumericGuard.EnsureFinite(
rawLongitudinalSpeedMetersPerSecond,
rawBodyVxMetersPerSecond,
"电机反馈车体纵向速度");
NumericGuard.EnsureFinite(
rawBodyVyMetersPerSecond,
"电机反馈车体横向速度");
var wheelSpeedTimestampSeconds =
_wheelSpeedClock.Elapsed.TotalSeconds;
var filteredLongitudinalSpeedMetersPerSecond =
UpdateLongitudinalSpeedFilter(
rawLongitudinalSpeedMetersPerSecond,
wheelSpeedTimestampSeconds,
out var hasValidWheelSpeedEstimate);
UpdateBodyVelocityFilters(
rawBodyVxMetersPerSecond,
rawBodyVyMetersPerSecond,
wheelSpeedTimestampSeconds,
out var filteredBodyVxMetersPerSecond,
out var filteredBodyVyMetersPerSecond,
out var hasValidWheelSpeedEstimate);
_latestDetourBodyVxMetersPerSecond =
poseState.TwistInBody.VxMetersPerSecond;
_latestDetourVelocityValid =
poseState.HasValidVelocityEstimate;
_latestRawWheelBodyVxMetersPerSecond =
rawLongitudinalSpeedMetersPerSecond;
rawBodyVxMetersPerSecond;
_latestFilteredWheelBodyVxMetersPerSecond =
filteredLongitudinalSpeedMetersPerSecond;
filteredBodyVxMetersPerSecond;
_latestRawWheelBodyVyMetersPerSecond =
rawBodyVyMetersPerSecond;
_latestFilteredWheelBodyVyMetersPerSecond =
filteredBodyVyMetersPerSecond;
_latestWheelVelocityValid =
hasValidWheelSpeedEstimate;
_hasVelocityDiagnostics = true;
// 第一阶段只替换控制器使用的车体纵向速度;横向速度和角速度
// 继续使用Detour估计,避免轮速差和舵角误差放大Vy与Omega噪声。
// 车体平面线速度来自四轮电机和舵角反馈;角速度继续使用Detour,
// 避免轮速差和舵角误差放大Omega噪声。
var twistInBody = new Twist2D(
filteredLongitudinalSpeedMetersPerSecond,
poseState.TwistInBody
.VyMetersPerSecond,
filteredBodyVxMetersPerSecond,
filteredBodyVyMetersPerSecond,
poseState.TwistInBody
.OmegaRadiansPerSecond);
@@ -151,13 +167,15 @@ namespace MultiWheelC.StateEstimation
}
/// <summary>
/// 读取最近一帧Detour纵向速度和轮速解算纵向速度,供实验记录使用。
/// 读取最近一帧Detour纵向速度和轮速解算平面速度,供实验记录使用。
/// </summary>
public bool TryGetLatestVelocityDiagnostics(
out double detourBodyVxMetersPerSecond,
out bool detourVelocityValid,
out double rawWheelBodyVxMetersPerSecond,
out double filteredWheelBodyVxMetersPerSecond,
out double rawWheelBodyVyMetersPerSecond,
out double filteredWheelBodyVyMetersPerSecond,
out bool wheelVelocityValid)
{
lock (_syncRoot)
@@ -170,6 +188,10 @@ namespace MultiWheelC.StateEstimation
_latestRawWheelBodyVxMetersPerSecond;
filteredWheelBodyVxMetersPerSecond =
_latestFilteredWheelBodyVxMetersPerSecond;
rawWheelBodyVyMetersPerSecond =
_latestRawWheelBodyVyMetersPerSecond;
filteredWheelBodyVyMetersPerSecond =
_latestFilteredWheelBodyVyMetersPerSecond;
wheelVelocityValid =
_latestWheelVelocityValid;
return _hasVelocityDiagnostics;
@@ -184,6 +206,7 @@ namespace MultiWheelC.StateEstimation
lock (_syncRoot)
{
_longitudinalSpeedFilter.Reset();
_lateralSpeedFilter.Reset();
_wheelSpeedClock.Restart();
_hasPreviousTimestamp = false;
_previousTimestampSeconds = 0.0;
@@ -192,17 +215,22 @@ namespace MultiWheelC.StateEstimation
_latestDetourVelocityValid = false;
_latestRawWheelBodyVxMetersPerSecond = 0.0;
_latestFilteredWheelBodyVxMetersPerSecond = 0.0;
_latestRawWheelBodyVyMetersPerSecond = 0.0;
_latestFilteredWheelBodyVyMetersPerSecond = 0.0;
_latestWheelVelocityValid = false;
LastFailureReason = string.Empty;
}
}
/// <summary>
/// 使用真实状态时间间隔更新纵向速度低通滤波,并在首帧建立基准。
/// 使用同一个真实采样间隔更新车体Vx和Vy低通滤波,并在首帧建立共同时间基准。
/// </summary>
private double UpdateLongitudinalSpeedFilter(
double rawLongitudinalSpeedMetersPerSecond,
private void UpdateBodyVelocityFilters(
double rawBodyVxMetersPerSecond,
double rawBodyVyMetersPerSecond,
double timestampSeconds,
out double filteredBodyVxMetersPerSecond,
out double filteredBodyVyMetersPerSecond,
out bool hasValidWheelSpeedEstimate)
{
NumericGuard.EnsureFiniteNonNegative(
@@ -212,11 +240,17 @@ namespace MultiWheelC.StateEstimation
if (!_hasPreviousTimestamp)
{
_longitudinalSpeedFilter.Reset(
rawLongitudinalSpeedMetersPerSecond);
rawBodyVxMetersPerSecond);
_lateralSpeedFilter.Reset(
rawBodyVyMetersPerSecond);
_previousTimestampSeconds = timestampSeconds;
_hasPreviousTimestamp = true;
hasValidWheelSpeedEstimate = false;
return rawLongitudinalSpeedMetersPerSecond;
filteredBodyVxMetersPerSecond =
rawBodyVxMetersPerSecond;
filteredBodyVyMetersPerSecond =
rawBodyVyMetersPerSecond;
return;
}
var deltaTimeSeconds =
@@ -227,15 +261,26 @@ namespace MultiWheelC.StateEstimation
if (deltaTimeSeconds <= 0.0)
{
_longitudinalSpeedFilter.Reset(
rawLongitudinalSpeedMetersPerSecond);
rawBodyVxMetersPerSecond);
_lateralSpeedFilter.Reset(
rawBodyVyMetersPerSecond);
hasValidWheelSpeedEstimate = false;
return rawLongitudinalSpeedMetersPerSecond;
filteredBodyVxMetersPerSecond =
rawBodyVxMetersPerSecond;
filteredBodyVyMetersPerSecond =
rawBodyVyMetersPerSecond;
return;
}
hasValidWheelSpeedEstimate = true;
return _longitudinalSpeedFilter.Update(
rawLongitudinalSpeedMetersPerSecond,
deltaTimeSeconds);
filteredBodyVxMetersPerSecond =
_longitudinalSpeedFilter.Update(
rawBodyVxMetersPerSecond,
deltaTimeSeconds);
filteredBodyVyMetersPerSecond =
_lateralSpeedFilter.Update(
rawBodyVyMetersPerSecond,
deltaTimeSeconds);
}
}