完善Detour状态估计与轨迹跟踪验证

This commit is contained in:
2026-08-19 17:38:17 +08:00
parent d8de901a80
commit 0d5539e595
55 changed files with 4473 additions and 387 deletions
@@ -24,6 +24,27 @@ namespace MultiWheelC
public double DetourX;
public double DetourY;
public double DetourTheta;
public long DetourTickRaw;
public double DetourLStep;
// Detour状态估计内部诊断;偏移量仅在跳变候选有效时有意义。
public bool HasDetourStateDiagnostics;
public bool DetourJumpCandidateActive;
public int DetourJumpCandidateConsistentFrameCount;
public double DetourEstimatedShiftDistanceMeters;
public double DetourEstimatedShiftHeadingRadians;
public int DetourAutomaticFrameShiftCount;
public string DetourStateStatusReason;
public double DetourDataAgeMilliseconds;
public double DetourSourceFrameIntervalMilliseconds;
public double DetourMotionPredictionTimestampSeconds;
public bool HasDetourInnovationDiagnostics;
public double DetourPositionInnovationMeters;
public double DetourAllowedPositionInnovationMeters;
public double DetourHeadingInnovationRadians;
public double DetourAllowedHeadingInnovationRadians;
public string DetourLastJumpTriggerReason;
public string DetourStateStatus;
// 车体速度单位为m/s,角速度统一使用rad/s。
public float CommandSpeed;
@@ -62,6 +83,10 @@ namespace MultiWheelC
public double WheelFeedbackRawBodyVyMetersPerSecond;
public double WheelFeedbackFilteredBodyVyMetersPerSecond;
public bool WheelFeedbackVelocityEstimateValid;
public bool HasWheelFeedbackAngularVelocityDiagnostics;
public double WheelFeedbackRawBodyOmegaRadiansPerSecond;
public double WheelFeedbackFilteredBodyOmegaRadiansPerSecond;
public double WheelFeedbackSampleTimestampSeconds;
// 四舵轮机械角使用deg,前后虚拟GCP命令角使用rad。
public bool HasSteeringDiagnostics;
@@ -130,6 +155,8 @@ namespace MultiWheelC
private readonly float _referenceDecelerationMetersPerSecondSquared;
private readonly int _sampleIntervalMs;
private readonly MultiWheelChassis _diagnosticChassis;
private readonly WheelFeedbackVehicleStateProvider
_diagnosticStateProvider;
private readonly List<TrackingSample> _samples =
new List<TrackingSample>();
@@ -203,7 +230,9 @@ namespace MultiWheelC
float referenceMotionFrameYawDegrees = 0f,
float referenceAccelerationMetersPerSecondSquared = 0f,
float referenceDecelerationMetersPerSecondSquared = 0f,
MultiWheelChassis diagnosticChassis = null)
MultiWheelChassis diagnosticChassis = null,
WheelFeedbackVehicleStateProvider
diagnosticStateProvider = null)
{
if (string.IsNullOrWhiteSpace(controllerName))
throw new ArgumentException(
@@ -235,6 +264,7 @@ namespace MultiWheelC
referenceDecelerationMetersPerSecondSquared;
_sampleIntervalMs = sampleIntervalMs;
_diagnosticChassis = diagnosticChassis;
_diagnosticStateProvider = diagnosticStateProvider;
}
// 保存成功后的CSV绝对路径;尚未保存时为空。
@@ -534,6 +564,47 @@ namespace MultiWheelC
var location =
DetourInterface.getCartLocation();
var hasDetourStateDiagnostics = false;
var detourJumpCandidateActive = false;
var detourJumpCandidateConsistentFrameCount = 0;
var detourEstimatedShiftDistanceMeters = 0.0;
var detourEstimatedShiftHeadingRadians = 0.0;
var detourAutomaticFrameShiftCount = 0;
var detourStateStatusReason = string.Empty;
var detourDataAgeMilliseconds = 0.0;
var detourSourceFrameIntervalSeconds = 0.0;
var detourMotionPredictionTimestampSeconds = 0.0;
var hasDetourInnovationDiagnostics = false;
var detourPositionInnovationMeters = 0.0;
var detourAllowedPositionInnovationMeters = 0.0;
var detourHeadingInnovationRadians = 0.0;
var detourAllowedHeadingInnovationRadians = 0.0;
var detourLastJumpTriggerReason = string.Empty;
var detourStateStatus = string.Empty;
if (_diagnosticStateProvider != null)
{
hasDetourStateDiagnostics =
_diagnosticStateProvider
.TryGetLatestDetourDiagnostics(
out detourJumpCandidateActive,
out detourJumpCandidateConsistentFrameCount,
out detourEstimatedShiftDistanceMeters,
out detourEstimatedShiftHeadingRadians,
out detourAutomaticFrameShiftCount,
out detourSourceFrameIntervalSeconds,
out detourMotionPredictionTimestampSeconds,
out hasDetourInnovationDiagnostics,
out detourPositionInnovationMeters,
out detourAllowedPositionInnovationMeters,
out detourHeadingInnovationRadians,
out detourAllowedHeadingInnovationRadians,
out detourLastJumpTriggerReason,
out detourStateStatus,
out detourStateStatusReason,
out detourDataAgeMilliseconds);
}
float commandSpeed;
float commandVx;
float commandVy;
@@ -556,6 +627,10 @@ namespace MultiWheelC
double wheelFeedbackRawBodyVyMetersPerSecond;
double wheelFeedbackFilteredBodyVyMetersPerSecond;
bool wheelFeedbackVelocityEstimateValid;
var hasWheelFeedbackAngularVelocityDiagnostics = false;
var wheelFeedbackRawBodyOmegaRadiansPerSecond = 0.0;
var wheelFeedbackFilteredBodyOmegaRadiansPerSecond = 0.0;
var wheelFeedbackSampleTimestampSeconds = 0.0;
bool hasGcpCommand;
double requestedFrontGcpAngleRadians;
double requestedRearGcpAngleRadians;
@@ -655,6 +730,46 @@ namespace MultiWheelC
_commandRearGcpAngleRadians;
}
// 直接从状态源读取最新完整轮组诊断,使原地自转等没有
// 控制周期回调的实验也能记录原始/滤波Vw及其采样时间。
if (_diagnosticStateProvider != null &&
_diagnosticStateProvider
.TryGetLatestVelocityDiagnostics(
out var directDetourBodyVx,
out var directDetourVelocityValid,
out var directRawWheelBodyVx,
out var directFilteredWheelBodyVx,
out var directRawWheelBodyVy,
out var directFilteredWheelBodyVy,
out var directRawWheelBodyOmega,
out var directFilteredWheelBodyOmega,
out var directWheelSampleTimestampSeconds,
out var directWheelVelocityValid))
{
hasVelocityDiagnostics = true;
detourEstimatedBodyVxMetersPerSecond =
directDetourBodyVx;
detourVelocityEstimateValid =
directDetourVelocityValid;
wheelFeedbackRawBodyVxMetersPerSecond =
directRawWheelBodyVx;
wheelFeedbackFilteredBodyVxMetersPerSecond =
directFilteredWheelBodyVx;
wheelFeedbackRawBodyVyMetersPerSecond =
directRawWheelBodyVy;
wheelFeedbackFilteredBodyVyMetersPerSecond =
directFilteredWheelBodyVy;
wheelFeedbackRawBodyOmegaRadiansPerSecond =
directRawWheelBodyOmega;
wheelFeedbackFilteredBodyOmegaRadiansPerSecond =
directFilteredWheelBodyOmega;
wheelFeedbackSampleTimestampSeconds =
directWheelSampleTimestampSeconds;
wheelFeedbackVelocityEstimateValid =
directWheelVelocityValid;
hasWheelFeedbackAngularVelocityDiagnostics = true;
}
var sample = new TrackingSample
{
ElapsedSeconds =
@@ -662,6 +777,45 @@ namespace MultiWheelC
DetourX = location.x,
DetourY = location.y,
DetourTheta = location.th,
DetourTickRaw = Convert.ToInt64(
location.tick,
CultureInfo.InvariantCulture),
DetourLStep = Convert.ToDouble(
location.l_step,
CultureInfo.InvariantCulture),
HasDetourStateDiagnostics =
hasDetourStateDiagnostics,
DetourJumpCandidateActive =
detourJumpCandidateActive,
DetourJumpCandidateConsistentFrameCount =
detourJumpCandidateConsistentFrameCount,
DetourEstimatedShiftDistanceMeters =
detourEstimatedShiftDistanceMeters,
DetourEstimatedShiftHeadingRadians =
detourEstimatedShiftHeadingRadians,
DetourAutomaticFrameShiftCount =
detourAutomaticFrameShiftCount,
DetourStateStatusReason =
detourStateStatusReason,
DetourDataAgeMilliseconds =
detourDataAgeMilliseconds,
DetourSourceFrameIntervalMilliseconds =
detourSourceFrameIntervalSeconds * 1000.0,
DetourMotionPredictionTimestampSeconds =
detourMotionPredictionTimestampSeconds,
HasDetourInnovationDiagnostics =
hasDetourInnovationDiagnostics,
DetourPositionInnovationMeters =
detourPositionInnovationMeters,
DetourAllowedPositionInnovationMeters =
detourAllowedPositionInnovationMeters,
DetourHeadingInnovationRadians =
detourHeadingInnovationRadians,
DetourAllowedHeadingInnovationRadians =
detourAllowedHeadingInnovationRadians,
DetourLastJumpTriggerReason =
detourLastJumpTriggerReason,
DetourStateStatus = detourStateStatus,
CommandSpeed = commandSpeed,
CommandVx = commandVx,
CommandVy = commandVy,
@@ -703,6 +857,14 @@ namespace MultiWheelC
wheelFeedbackFilteredBodyVyMetersPerSecond,
WheelFeedbackVelocityEstimateValid =
wheelFeedbackVelocityEstimateValid,
HasWheelFeedbackAngularVelocityDiagnostics =
hasWheelFeedbackAngularVelocityDiagnostics,
WheelFeedbackRawBodyOmegaRadiansPerSecond =
wheelFeedbackRawBodyOmegaRadiansPerSecond,
WheelFeedbackFilteredBodyOmegaRadiansPerSecond =
wheelFeedbackFilteredBodyOmegaRadiansPerSecond,
WheelFeedbackSampleTimestampSeconds =
wheelFeedbackSampleTimestampSeconds,
HasGcpCommand = hasGcpCommand,
RequestedFrontGcpAngleRadians =
requestedFrontGcpAngleRadians,
@@ -885,6 +1047,25 @@ namespace MultiWheelC
"DetourX," +
"DetourY," +
"DetourTheta," +
"DetourTickRaw," +
"DetourLStep," +
"HasDetourStateDiagnostics," +
"DetourJumpCandidateActive," +
"DetourJumpCandidateConsistentFrameCount," +
"DetourEstimatedShiftDistanceMeters," +
"DetourEstimatedShiftHeadingRadians," +
"DetourAutomaticFrameShiftCount," +
"DetourStateStatusReason," +
"DetourDataAgeMilliseconds," +
"DetourSourceFrameIntervalMilliseconds," +
"DetourMotionPredictionTimestampSeconds," +
"HasDetourInnovationDiagnostics," +
"DetourPositionInnovationMeters," +
"DetourAllowedPositionInnovationMeters," +
"DetourHeadingInnovationRadians," +
"DetourAllowedHeadingInnovationRadians," +
"DetourLastJumpTriggerReason," +
"DetourStateStatus," +
"CommandSpeed," +
// 保留旧列(deg/s)供历史Python脚本兼容。
"CommandAngularSpeed," +
@@ -928,6 +1109,10 @@ namespace MultiWheelC
"WheelFeedbackRawBodyVyMetersPerSecond," +
"WheelFeedbackFilteredBodyVyMetersPerSecond," +
"WheelFeedbackVelocityEstimateValid," +
"HasWheelFeedbackAngularVelocityDiagnostics," +
"WheelFeedbackRawBodyOmegaRadiansPerSecond," +
"WheelFeedbackFilteredBodyOmegaRadiansPerSecond," +
"WheelFeedbackSampleTimestampSeconds," +
"HasSteeringDiagnostics," +
"TargetSteerLeftFrontDegrees," +
"TargetSteerLeftRearDegrees," +
@@ -959,6 +1144,73 @@ namespace MultiWheelC
Format(sample.DetourX),
Format(sample.DetourY),
Format(sample.DetourTheta),
sample.DetourTickRaw.ToString(
CultureInfo.InvariantCulture),
Format(sample.DetourLStep),
sample.HasDetourStateDiagnostics
? "1"
: "0",
FormatOptionalBoolean(
sample.HasDetourStateDiagnostics,
sample.DetourJumpCandidateActive),
sample.HasDetourStateDiagnostics
? sample
.DetourJumpCandidateConsistentFrameCount
.ToString(
CultureInfo.InvariantCulture)
: string.Empty,
FormatOptional(
sample.HasDetourStateDiagnostics &&
sample.DetourJumpCandidateActive,
sample.DetourEstimatedShiftDistanceMeters),
FormatOptional(
sample.HasDetourStateDiagnostics &&
sample.DetourJumpCandidateActive,
sample.DetourEstimatedShiftHeadingRadians),
sample.HasDetourStateDiagnostics
? sample.DetourAutomaticFrameShiftCount
.ToString(
CultureInfo.InvariantCulture)
: string.Empty,
sample.HasDetourStateDiagnostics
? EscapeCsv(
sample.DetourStateStatusReason)
: string.Empty,
FormatOptional(
sample.HasDetourStateDiagnostics,
sample.DetourDataAgeMilliseconds),
FormatOptional(
sample.HasDetourStateDiagnostics,
sample.DetourSourceFrameIntervalMilliseconds),
FormatOptional(
sample.HasDetourStateDiagnostics,
sample.DetourMotionPredictionTimestampSeconds),
FormatOptionalBoolean(
sample.HasDetourStateDiagnostics,
sample.HasDetourInnovationDiagnostics),
FormatOptional(
sample.HasDetourStateDiagnostics &&
sample.HasDetourInnovationDiagnostics,
sample.DetourPositionInnovationMeters),
FormatOptional(
sample.HasDetourStateDiagnostics &&
sample.HasDetourInnovationDiagnostics,
sample.DetourAllowedPositionInnovationMeters),
FormatOptional(
sample.HasDetourStateDiagnostics &&
sample.HasDetourInnovationDiagnostics,
sample.DetourHeadingInnovationRadians),
FormatOptional(
sample.HasDetourStateDiagnostics &&
sample.HasDetourInnovationDiagnostics,
sample.DetourAllowedHeadingInnovationRadians),
sample.HasDetourStateDiagnostics
? EscapeCsv(
sample.DetourLastJumpTriggerReason)
: string.Empty,
sample.HasDetourStateDiagnostics
? EscapeCsv(sample.DetourStateStatus)
: string.Empty,
Format(sample.CommandSpeed),
Format(
AngleMath.RadiansToDegrees(
@@ -1067,6 +1319,21 @@ namespace MultiWheelC
? "1"
: "0"
: string.Empty,
FormatOptionalBoolean(
sample.HasVelocityDiagnostics,
sample.HasWheelFeedbackAngularVelocityDiagnostics),
FormatOptional(
sample.HasVelocityDiagnostics &&
sample.HasWheelFeedbackAngularVelocityDiagnostics,
sample.WheelFeedbackRawBodyOmegaRadiansPerSecond),
FormatOptional(
sample.HasVelocityDiagnostics &&
sample.HasWheelFeedbackAngularVelocityDiagnostics,
sample.WheelFeedbackFilteredBodyOmegaRadiansPerSecond),
FormatOptional(
sample.HasVelocityDiagnostics &&
sample.HasWheelFeedbackAngularVelocityDiagnostics,
sample.WheelFeedbackSampleTimestampSeconds),
sample.HasSteeringDiagnostics
? "1"
: "0",