增加车队轨迹控制核心与轮组自转模式

This commit is contained in:
2026-08-21 17:33:29 +08:00
parent fea2265e2d
commit 0ab409cd2a
33 changed files with 1949 additions and 989 deletions
@@ -96,67 +96,19 @@ namespace MultiWheelC.StateEstimation
{
try
{
var actualCarSpeed =
_chassis.GetCarSpeed(true);
var rawBodyVxMetersPerSecond =
(double)actualCarSpeed.Vx;
var rawBodyVyMetersPerSecond =
(double)actualCarSpeed.Vy;
// CommonUsage.CarSpeed.Vw在旧底盘边界使用deg/s
// 状态估计内部统一转换为rad/s。
var rawBodyOmegaRadiansPerSecond =
AngleMath.DegreesToRadians(
actualCarSpeed.Vw);
NumericGuard.EnsureFinite(
rawBodyVxMetersPerSecond,
"电机反馈车体纵向速度");
NumericGuard.EnsureFinite(
rawBodyVyMetersPerSecond,
"电机反馈车体横向速度");
NumericGuard.EnsureFinite(
rawBodyOmegaRadiansPerSecond,
"电机反馈车体角速度");
var wheelSpeedTimestampSeconds =
_wheelSpeedClock.Elapsed.TotalSeconds;
UpdateBodyVelocityFilters(
rawBodyVxMetersPerSecond,
rawBodyVyMetersPerSecond,
rawBodyOmegaRadiansPerSecond,
wheelSpeedTimestampSeconds,
out var filteredBodyVxMetersPerSecond,
out var filteredBodyVyMetersPerSecond,
out var filteredBodyOmegaRadiansPerSecond,
ReadFilteredWheelTwist(
out var filteredWheelTwist,
out _,
out var hasValidWheelSpeedEstimate);
_latestRawWheelBodyVxMetersPerSecond =
rawBodyVxMetersPerSecond;
_latestFilteredWheelBodyVxMetersPerSecond =
filteredBodyVxMetersPerSecond;
_latestRawWheelBodyVyMetersPerSecond =
rawBodyVyMetersPerSecond;
_latestFilteredWheelBodyVyMetersPerSecond =
filteredBodyVyMetersPerSecond;
_latestRawWheelBodyOmegaRadiansPerSecond =
rawBodyOmegaRadiansPerSecond;
_latestFilteredWheelBodyOmegaRadiansPerSecond =
filteredBodyOmegaRadiansPerSecond;
_latestWheelSampleTimestampSeconds =
wheelSpeedTimestampSeconds;
_latestWheelVelocityValid =
hasValidWheelSpeedEstimate;
_latestWheelFeedbackReadSucceeded = true;
// Detour位姿跳变确认期间需要用轮速维持短时运动预测。
if (_poseProvider is DetourVehicleStateProvider
detourStateProvider)
{
detourStateProvider.UpdateWheelVelocityEstimate(
filteredBodyVxMetersPerSecond,
filteredBodyVyMetersPerSecond,
filteredBodyOmegaRadiansPerSecond,
filteredWheelTwist.VxMetersPerSecond,
filteredWheelTwist.VyMetersPerSecond,
filteredWheelTwist.OmegaRadiansPerSecond,
hasValidWheelSpeedEstimate);
}
@@ -175,13 +127,18 @@ namespace MultiWheelC.StateEstimation
poseState.HasValidVelocityEstimate;
_hasVelocityDiagnostics = true;
// 车体平面线速度来自四轮电机和舵角反馈;角速度继续使用Detour,
// 避免轮速差和舵角误差放大Omega噪声
// 轮组反馈有效后统一使用滤波后的平面速度;初始化期间
// 暂时保留Detour角速度作为回退值
var omegaRadiansPerSecond =
hasValidWheelSpeedEstimate
? filteredWheelTwist
.OmegaRadiansPerSecond
: poseState.TwistInBody
.OmegaRadiansPerSecond;
var twistInBody = new Twist2D(
filteredBodyVxMetersPerSecond,
filteredBodyVyMetersPerSecond,
poseState.TwistInBody
.OmegaRadiansPerSecond);
filteredWheelTwist.VxMetersPerSecond,
filteredWheelTwist.VyMetersPerSecond,
omegaRadiansPerSecond);
var twistInWorld =
FrameTransform2D
@@ -210,6 +167,47 @@ namespace MultiWheelC.StateEstimation
}
}
/// <summary>
/// 读取并滤波轮组反馈速度,不访问Detour;首帧仅建立滤波时间基准并返回false。
/// </summary>
public bool TryGetWheelTwist(
out Twist2D twistInBody,
out double sampleTimestampSeconds)
{
lock (_syncRoot)
{
try
{
ReadFilteredWheelTwist(
out var filteredWheelTwist,
out sampleTimestampSeconds,
out var hasValidWheelSpeedEstimate);
if (!hasValidWheelSpeedEstimate)
{
twistInBody = Twist2D.Zero;
LastFailureReason =
"轮组速度估计正在建立采样时间基准。";
return false;
}
twistInBody = filteredWheelTwist;
LastFailureReason = string.Empty;
return true;
}
catch (Exception exception)
{
_latestWheelFeedbackReadSucceeded = false;
twistInBody = Twist2D.Zero;
sampleTimestampSeconds = 0.0;
LastFailureReason =
"舵轮电机反馈车体速度解算失败:" +
exception.Message;
return false;
}
}
}
/// <summary>
/// 读取Detour独立校验后的航向,同时保持轮组速度预测输入更新。
/// </summary>
@@ -524,6 +522,73 @@ namespace MultiWheelC.StateEstimation
}
}
/// <summary>
/// 从底盘读取一次轮组速度,统一转换为SI单位并更新共用低通滤波状态。
/// </summary>
private void ReadFilteredWheelTwist(
out Twist2D filteredTwistInBody,
out double sampleTimestampSeconds,
out bool hasValidWheelSpeedEstimate)
{
var actualCarSpeed =
_chassis.GetCarSpeed(true);
var rawBodyVxMetersPerSecond =
(double)actualCarSpeed.Vx;
var rawBodyVyMetersPerSecond =
(double)actualCarSpeed.Vy;
// CommonUsage.CarSpeed.Vw在旧底盘边界使用deg/s
// 状态估计内部统一转换为rad/s。
var rawBodyOmegaRadiansPerSecond =
AngleMath.DegreesToRadians(
actualCarSpeed.Vw);
NumericGuard.EnsureFinite(
rawBodyVxMetersPerSecond,
"电机反馈车体纵向速度");
NumericGuard.EnsureFinite(
rawBodyVyMetersPerSecond,
"电机反馈车体横向速度");
NumericGuard.EnsureFinite(
rawBodyOmegaRadiansPerSecond,
"电机反馈车体角速度");
sampleTimestampSeconds =
_wheelSpeedClock.Elapsed.TotalSeconds;
UpdateBodyVelocityFilters(
rawBodyVxMetersPerSecond,
rawBodyVyMetersPerSecond,
rawBodyOmegaRadiansPerSecond,
sampleTimestampSeconds,
out var filteredBodyVxMetersPerSecond,
out var filteredBodyVyMetersPerSecond,
out var filteredBodyOmegaRadiansPerSecond,
out hasValidWheelSpeedEstimate);
filteredTwistInBody = new Twist2D(
filteredBodyVxMetersPerSecond,
filteredBodyVyMetersPerSecond,
filteredBodyOmegaRadiansPerSecond);
_latestRawWheelBodyVxMetersPerSecond =
rawBodyVxMetersPerSecond;
_latestFilteredWheelBodyVxMetersPerSecond =
filteredBodyVxMetersPerSecond;
_latestRawWheelBodyVyMetersPerSecond =
rawBodyVyMetersPerSecond;
_latestFilteredWheelBodyVyMetersPerSecond =
filteredBodyVyMetersPerSecond;
_latestRawWheelBodyOmegaRadiansPerSecond =
rawBodyOmegaRadiansPerSecond;
_latestFilteredWheelBodyOmegaRadiansPerSecond =
filteredBodyOmegaRadiansPerSecond;
_latestWheelSampleTimestampSeconds =
sampleTimestampSeconds;
_latestWheelVelocityValid =
hasValidWheelSpeedEstimate;
_latestWheelFeedbackReadSucceeded = true;
}
/// <summary>
/// 使用同一个真实采样间隔更新车体Vx、Vy和Omega低通滤波,并在首帧建立共同时间基准。
/// </summary>