diff --git a/MedullaAdapter/build/Medulla/plugins/CommonUsage.dll b/MedullaAdapter/build/Medulla/plugins/CommonUsage.dll
index 128c1e7..cc330a2 100644
Binary files a/MedullaAdapter/build/Medulla/plugins/CommonUsage.dll and b/MedullaAdapter/build/Medulla/plugins/CommonUsage.dll differ
diff --git a/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.dll b/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.dll
index 059ddf8..34dc787 100644
Binary files a/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.dll and b/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.dll differ
diff --git a/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.pdb b/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.pdb
index 51a851a..1e174f5 100644
Binary files a/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.pdb and b/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.pdb differ
diff --git a/MultiWheelC/Configuration/PilotConfig.ParkingControl.cs b/MultiWheelC/Configuration/PilotConfig.ParkingControl.cs
index 1cd5fce..a041b4d 100644
--- a/MultiWheelC/Configuration/PilotConfig.ParkingControl.cs
+++ b/MultiWheelC/Configuration/PilotConfig.ParkingControl.cs
@@ -94,7 +94,7 @@ public partial class PilotConfig
#region 停车控制-原地自转
[FieldMember(desc = "停车控制:原地自转Kp")]
- public float InPlaceRotateKp = 1.05f;
+ public float InPlaceRotateKp = 1.03f;
[FieldMember(desc = "停车控制:原地自转Ki")]
public float InPlaceRotateKi = 0f;
diff --git a/MultiWheelC/Control/Execution/ParkingGeometricController.cs b/MultiWheelC/Control/Execution/ParkingGeometricController.cs
index da4ca60..0e3573a 100644
--- a/MultiWheelC/Control/Execution/ParkingGeometricController.cs
+++ b/MultiWheelC/Control/Execution/ParkingGeometricController.cs
@@ -271,7 +271,12 @@ namespace MultiWheelC.Control.Execution
public TrajectoryProjection? LastProjection { get; private set; }
///
- /// 获取最近一次发送或准备发送的GCP运动命令。
+ /// 获取最近一次由控制器和分配器生成、尚未经过角速度限制的GCP运动命令。
+ ///
+ public GcpMotionCommand? LastRequestedCommand { get; private set; }
+
+ ///
+ /// 获取最近一次经过GCP角速度限制后实际发送给底盘的运动命令。
///
public GcpMotionCommand? LastCommand { get; private set; }
@@ -488,6 +493,7 @@ namespace MultiWheelC.Control.Execution
var gcpCommand = _gcpAllocator.Allocate(
commandSpeedMetersPerSecond,
lateralCommand);
+ LastRequestedCommand = gcpCommand;
controllerComputeMilliseconds =
GetElapsedMilliseconds(
@@ -942,6 +948,7 @@ namespace MultiWheelC.Control.Execution
_commandExecutor.Stop();
_lateralController.Reset();
_longitudinalController.Reset();
+ LastRequestedCommand = null;
LastCommand = null;
LastFailureReason =
"当前无法获得有效车辆状态,底盘已停车并等待定位恢复。";
@@ -956,6 +963,10 @@ namespace MultiWheelC.Control.Execution
StopAndResetControllers();
IsActive = false;
IsCompleted = true;
+ LastRequestedCommand = new GcpMotionCommand(
+ 0.0,
+ 0.0,
+ 0.0);
LastCommand = new GcpMotionCommand(
0.0,
0.0,
@@ -974,6 +985,7 @@ namespace MultiWheelC.Control.Execution
StopAndResetControllers();
IsActive = false;
IsCompleted = false;
+ LastRequestedCommand = null;
LastCommand = null;
LastFailureReason = reason;
LastException = exception;
@@ -1000,6 +1012,7 @@ namespace MultiWheelC.Control.Execution
_previousStateTimestampSeconds = 0.0;
LastVehicleState = null;
LastProjection = null;
+ LastRequestedCommand = null;
LastCommand = null;
LastControlReferenceSpeedMetersPerSecond = null;
LastCurvaturePreviewDistanceMeters = null;
diff --git a/MultiWheelC/Experiments/CompositeMotionPlanTests.cs b/MultiWheelC/Experiments/CompositeMotionPlanTests.cs
index c9f6914..f7a9a1d 100644
--- a/MultiWheelC/Experiments/CompositeMotionPlanTests.cs
+++ b/MultiWheelC/Experiments/CompositeMotionPlanTests.cs
@@ -291,7 +291,9 @@ namespace MultiWheelC
{
_recorder?.RecordControlCycleTiming(
controller.LastCycleTiming.Value,
- motionSegmentIndex);
+ motionSegmentIndex,
+ controller.LastRequestedCommand,
+ controller.LastCommand);
}
if (controller.LastVehicleState.HasValue)
@@ -338,8 +340,13 @@ namespace MultiWheelC
projection.ReferencePoint.CurvaturePerMeter);
}
+ var requestedCommand =
+ controller.LastRequestedCommand ??
+ controller.LastCommand.Value;
var command = controller.LastCommand.Value;
_recorder?.UpdateGcpCommand(
+ requestedCommand.FrontAngleRadians,
+ requestedCommand.RearAngleRadians,
command.FrontAngleRadians,
command.RearAngleRadians);
var curvaturePerMeter = Math.Tan(
diff --git a/MultiWheelC/Experiments/NewControllerTrackingTests.cs b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
index fcdb842..ad06f9a 100644
--- a/MultiWheelC/Experiments/NewControllerTrackingTests.cs
+++ b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
@@ -94,7 +94,7 @@ namespace MultiWheelC
/// 从当前Detour位姿开始执行新版控制器4m直线跟踪并保存实验数据。
///
[MovementTest(name = "新版控制器:4m直线轨迹跟踪")]
- public sealed class NewControllerStraight4mTest
+ public class NewControllerStraight4mTest
: MovementTest
{
private const float MillimetersPerMeter = 1000f;
@@ -131,6 +131,12 @@ namespace MultiWheelC
///
public double PointSpacingMeters = 0.02;
+ ///
+ /// 获取实验记录使用的轨迹基础名称,供同一套直线测试流程区分前进和倒车。
+ ///
+ protected virtual string ExperimentTrajectoryBaseName =>
+ "ProfiledStraight4m";
+
///
/// 读取当前位姿、绘制离散轨迹并启动新版轨迹跟踪动作。
///
@@ -197,7 +203,7 @@ namespace MultiWheelC
controllerName: "NewStanleyPid",
trajectoryName:
TrajectoryExperimentInput.BuildTrajectoryName(
- "ProfiledStraight4m",
+ ExperimentTrajectoryBaseName,
lateralOffsetMeters),
trialNumber: TrialNumber,
referenceStart: referenceStart,
@@ -336,7 +342,11 @@ namespace MultiWheelC
if (controller.LastCycleTiming.HasValue)
{
recorder.RecordControlCycleTiming(
- controller.LastCycleTiming.Value);
+ controller.LastCycleTiming.Value,
+ requestedCommand:
+ controller.LastRequestedCommand,
+ sentCommand:
+ controller.LastCommand);
}
if (controller.LastVehicleState.HasValue)
@@ -371,8 +381,13 @@ namespace MultiWheelC
projection.ReferencePoint.CurvaturePerMeter);
}
+ var requestedCommand =
+ controller.LastRequestedCommand ??
+ controller.LastCommand.Value;
var command = controller.LastCommand.Value;
recorder.UpdateGcpCommand(
+ requestedCommand.FrontAngleRadians,
+ requestedCommand.RearAngleRadians,
command.FrontAngleRadians,
command.RearAngleRadians);
var curvaturePerMeter = Math.Tan(
@@ -430,6 +445,28 @@ namespace MultiWheelC
}
+ ///
+ /// 从当前Detour位姿开始,沿车体后方执行新版控制器4m直线倒车跟踪并保存实验数据。
+ ///
+ [MovementTest(name = "新版控制器:4m直线倒车轨迹跟踪")]
+ public sealed class NewControllerReverseStraight4mTest
+ : NewControllerStraight4mTest
+ {
+ ///
+ /// 使用负参考速度,使轨迹工厂沿车尾方向生成轨迹并触发倒车控制语义。
+ ///
+ public NewControllerReverseStraight4mTest()
+ {
+ CruiseSpeedMetersPerSecond = -0.40;
+ }
+
+ ///
+ /// 将倒车实验与前进直线实验的CSV名称明确区分。
+ ///
+ protected override string ExperimentTrajectoryBaseName =>
+ "ProfiledReverseStraight4m";
+ }
+
///
/// 从当前Detour位姿开始执行“3m直线—左半圆—3m直线”新版控制器跟踪实验。
///
@@ -702,7 +739,11 @@ namespace MultiWheelC
if (controller.LastCycleTiming.HasValue)
{
recorder.RecordControlCycleTiming(
- controller.LastCycleTiming.Value);
+ controller.LastCycleTiming.Value,
+ requestedCommand:
+ controller.LastRequestedCommand,
+ sentCommand:
+ controller.LastCommand);
}
if (controller.LastVehicleState.HasValue)
@@ -737,8 +778,13 @@ namespace MultiWheelC
projection.ReferencePoint.CurvaturePerMeter);
}
+ var requestedCommand =
+ controller.LastRequestedCommand ??
+ controller.LastCommand.Value;
var command = controller.LastCommand.Value;
recorder.UpdateGcpCommand(
+ requestedCommand.FrontAngleRadians,
+ requestedCommand.RearAngleRadians,
command.FrontAngleRadians,
command.RearAngleRadians);
var curvaturePerMeter = Math.Tan(
diff --git a/MultiWheelC/Experiments/TrackingExperimentRecorder.cs b/MultiWheelC/Experiments/TrackingExperimentRecorder.cs
index 564e5c0..95fc6fd 100644
--- a/MultiWheelC/Experiments/TrackingExperimentRecorder.cs
+++ b/MultiWheelC/Experiments/TrackingExperimentRecorder.cs
@@ -9,6 +9,7 @@ using System.Text;
using System.Threading;
using CommonUsage.Chassis;
using MyParking.Shared;
+using MultiWheelC.Control.Allocation;
using MultiWheelC.Control.Execution;
using MultiWheelC.StateEstimation;
@@ -71,6 +72,13 @@ namespace MultiWheelC
public double ActualSteerRightFrontDegrees;
public double ActualSteerRightRearDegrees;
public bool HasGcpCommand;
+ public double RequestedFrontGcpAngleRadians;
+ public double RequestedRearGcpAngleRadians;
+ public double SentFrontGcpAngleRadians;
+ public double SentRearGcpAngleRadians;
+ public bool FrontGcpRateLimitActive;
+ public bool RearGcpRateLimitActive;
+ // 兼容既有分析脚本:Command角仍表示限速后实际发送角。
public double CommandFrontGcpAngleRadians;
public double CommandRearGcpAngleRadians;
}
@@ -86,19 +94,28 @@ namespace MultiWheelC
public ControlCycleTimingRecord(
double recorderElapsedSeconds,
int motionSegmentIndex,
- ParkingControlCycleTiming timing)
+ ParkingControlCycleTiming timing,
+ GcpMotionCommand? requestedCommand,
+ GcpMotionCommand? sentCommand)
{
RecorderElapsedSeconds =
recorderElapsedSeconds;
MotionSegmentIndex = motionSegmentIndex;
Timing = timing;
+ RequestedCommand = requestedCommand;
+ SentCommand = sentCommand;
}
public double RecorderElapsedSeconds { get; }
public int MotionSegmentIndex { get; }
public ParkingControlCycleTiming Timing { get; }
+ public GcpMotionCommand? RequestedCommand { get; }
+ public GcpMotionCommand? SentCommand { get; }
}
+ private const double GcpAngleComparisonToleranceRadians =
+ 1e-9;
+
private readonly string _controllerName;
private readonly string _trajectoryName;
private readonly int _trialNumber;
@@ -161,6 +178,12 @@ namespace MultiWheelC
private double _wheelFeedbackFilteredBodyVxMetersPerSecond;
private bool _wheelFeedbackVelocityEstimateValid;
private bool _hasGcpCommand;
+ private double _requestedFrontGcpAngleRadians;
+ private double _requestedRearGcpAngleRadians;
+ private double _sentFrontGcpAngleRadians;
+ private double _sentRearGcpAngleRadians;
+ private bool _frontGcpRateLimitActive;
+ private bool _rearGcpRateLimitActive;
private double _commandFrontGcpAngleRadians;
private double _commandRearGcpAngleRadians;
@@ -288,12 +311,16 @@ namespace MultiWheelC
///
public void RecordControlCycleTiming(
ParkingControlCycleTiming timing,
- int motionSegmentIndex = -1)
+ int motionSegmentIndex = -1,
+ GcpMotionCommand? requestedCommand = null,
+ GcpMotionCommand? sentCommand = null)
{
var record = new ControlCycleTimingRecord(
_stopwatch.Elapsed.TotalSeconds,
motionSegmentIndex,
- timing);
+ timing,
+ requestedCommand,
+ sentCommand);
lock (_controlCycleTimingSyncRoot)
{
@@ -339,18 +366,37 @@ namespace MultiWheelC
}
///
- /// 保存经过GCP角速度限制后实际交给底盘的前后虚拟控制点转角。
+ /// 保存本周期经过GCP角速度限制前后的前后虚拟控制点转角。
///
public void UpdateGcpCommand(
- double frontGcpAngleRadians,
- double rearGcpAngleRadians)
+ double requestedFrontGcpAngleRadians,
+ double requestedRearGcpAngleRadians,
+ double sentFrontGcpAngleRadians,
+ double sentRearGcpAngleRadians)
{
lock (_stateSyncRoot)
{
+ _requestedFrontGcpAngleRadians =
+ requestedFrontGcpAngleRadians;
+ _requestedRearGcpAngleRadians =
+ requestedRearGcpAngleRadians;
+ _sentFrontGcpAngleRadians =
+ sentFrontGcpAngleRadians;
+ _sentRearGcpAngleRadians =
+ sentRearGcpAngleRadians;
+ _frontGcpRateLimitActive =
+ IsGcpRateLimitActive(
+ requestedFrontGcpAngleRadians,
+ sentFrontGcpAngleRadians);
+ _rearGcpRateLimitActive =
+ IsGcpRateLimitActive(
+ requestedRearGcpAngleRadians,
+ sentRearGcpAngleRadians);
+ // 保留原字段语义,避免既有绘图脚本失效。
_commandFrontGcpAngleRadians =
- frontGcpAngleRadians;
+ sentFrontGcpAngleRadians;
_commandRearGcpAngleRadians =
- rearGcpAngleRadians;
+ sentRearGcpAngleRadians;
_hasGcpCommand = true;
}
}
@@ -499,6 +545,12 @@ namespace MultiWheelC
double wheelFeedbackFilteredBodyVxMetersPerSecond;
bool wheelFeedbackVelocityEstimateValid;
bool hasGcpCommand;
+ double requestedFrontGcpAngleRadians;
+ double requestedRearGcpAngleRadians;
+ double sentFrontGcpAngleRadians;
+ double sentRearGcpAngleRadians;
+ bool frontGcpRateLimitActive;
+ bool rearGcpRateLimitActive;
double commandFrontGcpAngleRadians;
double commandRearGcpAngleRadians;
@@ -569,6 +621,18 @@ namespace MultiWheelC
wheelFeedbackVelocityEstimateValid =
_wheelFeedbackVelocityEstimateValid;
hasGcpCommand = _hasGcpCommand;
+ requestedFrontGcpAngleRadians =
+ _requestedFrontGcpAngleRadians;
+ requestedRearGcpAngleRadians =
+ _requestedRearGcpAngleRadians;
+ sentFrontGcpAngleRadians =
+ _sentFrontGcpAngleRadians;
+ sentRearGcpAngleRadians =
+ _sentRearGcpAngleRadians;
+ frontGcpRateLimitActive =
+ _frontGcpRateLimitActive;
+ rearGcpRateLimitActive =
+ _rearGcpRateLimitActive;
commandFrontGcpAngleRadians =
_commandFrontGcpAngleRadians;
commandRearGcpAngleRadians =
@@ -620,6 +684,18 @@ namespace MultiWheelC
WheelFeedbackVelocityEstimateValid =
wheelFeedbackVelocityEstimateValid,
HasGcpCommand = hasGcpCommand,
+ RequestedFrontGcpAngleRadians =
+ requestedFrontGcpAngleRadians,
+ RequestedRearGcpAngleRadians =
+ requestedRearGcpAngleRadians,
+ SentFrontGcpAngleRadians =
+ sentFrontGcpAngleRadians,
+ SentRearGcpAngleRadians =
+ sentRearGcpAngleRadians,
+ FrontGcpRateLimitActive =
+ frontGcpRateLimitActive,
+ RearGcpRateLimitActive =
+ rearGcpRateLimitActive,
CommandFrontGcpAngleRadians =
commandFrontGcpAngleRadians,
CommandRearGcpAngleRadians =
@@ -841,7 +917,13 @@ namespace MultiWheelC
"ActualSteerRightRearDegrees," +
"HasGcpCommand," +
"CommandFrontGcpAngleRadians," +
- "CommandRearGcpAngleRadians");
+ "CommandRearGcpAngleRadians," +
+ "RequestedFrontGcpAngleRadians," +
+ "RequestedRearGcpAngleRadians," +
+ "SentFrontGcpAngleRadians," +
+ "SentRearGcpAngleRadians," +
+ "FrontGcpRateLimitActive," +
+ "RearGcpRateLimitActive");
foreach (var sample in snapshot)
{
@@ -992,7 +1074,25 @@ namespace MultiWheelC
sample.CommandFrontGcpAngleRadians),
FormatOptional(
sample.HasGcpCommand,
- sample.CommandRearGcpAngleRadians)));
+ sample.CommandRearGcpAngleRadians),
+ FormatOptional(
+ sample.HasGcpCommand,
+ sample.RequestedFrontGcpAngleRadians),
+ FormatOptional(
+ sample.HasGcpCommand,
+ sample.RequestedRearGcpAngleRadians),
+ FormatOptional(
+ sample.HasGcpCommand,
+ sample.SentFrontGcpAngleRadians),
+ FormatOptional(
+ sample.HasGcpCommand,
+ sample.SentRearGcpAngleRadians),
+ FormatOptionalBoolean(
+ sample.HasGcpCommand,
+ sample.FrontGcpRateLimitActive),
+ FormatOptionalBoolean(
+ sample.HasGcpCommand,
+ sample.RearGcpRateLimitActive)));
}
}
}
@@ -1046,7 +1146,14 @@ namespace MultiWheelC
"HasStateTimestamp," +
"StateTimestampSeconds," +
"StateTimestampChanged," +
- "CycleResult");
+ "CycleResult," +
+ "HasGcpCommand," +
+ "RequestedFrontGcpAngleRadians," +
+ "RequestedRearGcpAngleRadians," +
+ "SentFrontGcpAngleRadians," +
+ "SentRearGcpAngleRadians," +
+ "FrontGcpRateLimitActive," +
+ "RearGcpRateLimitActive");
foreach (var record in snapshot)
{
@@ -1060,6 +1167,23 @@ namespace MultiWheelC
0.0,
timing.TotalCycleMilliseconds -
measuredStageMilliseconds);
+ var hasGcpCommand =
+ record.RequestedCommand.HasValue &&
+ record.SentCommand.HasValue;
+ var requestedCommand =
+ record.RequestedCommand.GetValueOrDefault();
+ var sentCommand =
+ record.SentCommand.GetValueOrDefault();
+ var frontRateLimitActive =
+ hasGcpCommand &&
+ IsGcpRateLimitActive(
+ requestedCommand.FrontAngleRadians,
+ sentCommand.FrontAngleRadians);
+ var rearRateLimitActive =
+ hasGcpCommand &&
+ IsGcpRateLimitActive(
+ requestedCommand.RearAngleRadians,
+ sentCommand.RearAngleRadians);
writer.WriteLine(string.Join(
",",
@@ -1092,7 +1216,28 @@ namespace MultiWheelC
? "1"
: "0"
: string.Empty,
- EscapeCsv(timing.Result.ToString())));
+ EscapeCsv(timing.Result.ToString()),
+ hasGcpCommand
+ ? "1"
+ : "0",
+ FormatOptional(
+ hasGcpCommand,
+ requestedCommand.FrontAngleRadians),
+ FormatOptional(
+ hasGcpCommand,
+ requestedCommand.RearAngleRadians),
+ FormatOptional(
+ hasGcpCommand,
+ sentCommand.FrontAngleRadians),
+ FormatOptional(
+ hasGcpCommand,
+ sentCommand.RearAngleRadians),
+ FormatOptionalBoolean(
+ hasGcpCommand,
+ frontRateLimitActive),
+ FormatOptionalBoolean(
+ hasGcpCommand,
+ rearRateLimitActive)));
}
}
}
@@ -1133,6 +1278,33 @@ namespace MultiWheelC
: string.Empty;
}
+ ///
+ /// 在GCP命令有效时将布尔诊断输出为0或1,否则保持CSV空值。
+ ///
+ private static string FormatOptionalBoolean(
+ bool hasValue,
+ bool value)
+ {
+ return hasValue
+ ? value
+ ? "1"
+ : "0"
+ : string.Empty;
+ }
+
+ ///
+ /// 判断GCP角速度限制是否实质改变了控制器请求角度。
+ ///
+ private static bool IsGcpRateLimitActive(
+ double requestedAngleRadians,
+ double sentAngleRadians)
+ {
+ return Math.Abs(
+ requestedAngleRadians -
+ sentAngleRadians) >
+ GcpAngleComparisonToleranceRadians;
+ }
+
// 对CSV文本字段进行引号和逗号转义。
private static string EscapeCsv(string value)
{
diff --git a/MultiWheelC/build/Clumsy/CommonUsage.dll b/MultiWheelC/build/Clumsy/CommonUsage.dll
index 128c1e7..cc330a2 100644
Binary files a/MultiWheelC/build/Clumsy/CommonUsage.dll and b/MultiWheelC/build/Clumsy/CommonUsage.dll differ
diff --git a/MultiWheelC/build/Clumsy/MultiWheelC.dll b/MultiWheelC/build/Clumsy/MultiWheelC.dll
index 11b41a8..98814d9 100644
Binary files a/MultiWheelC/build/Clumsy/MultiWheelC.dll and b/MultiWheelC/build/Clumsy/MultiWheelC.dll differ
diff --git a/MultiWheelC/build/Clumsy/MultiWheelC.pdb b/MultiWheelC/build/Clumsy/MultiWheelC.pdb
index 8ab6e1c..07890c2 100644
Binary files a/MultiWheelC/build/Clumsy/MultiWheelC.pdb and b/MultiWheelC/build/Clumsy/MultiWheelC.pdb differ
diff --git a/output/C/CommonUsage.dll b/output/C/CommonUsage.dll
index 128c1e7..cc330a2 100644
Binary files a/output/C/CommonUsage.dll and b/output/C/CommonUsage.dll differ
diff --git a/output/C/MultiWheelC.dll b/output/C/MultiWheelC.dll
index 11b41a8..98814d9 100644
Binary files a/output/C/MultiWheelC.dll and b/output/C/MultiWheelC.dll differ
diff --git a/output/C/MultiWheelC.pdb b/output/C/MultiWheelC.pdb
index 8ab6e1c..07890c2 100644
Binary files a/output/C/MultiWheelC.pdb and b/output/C/MultiWheelC.pdb differ
diff --git a/output/M/CommonUsage.dll b/output/M/CommonUsage.dll
index 128c1e7..cc330a2 100644
Binary files a/output/M/CommonUsage.dll and b/output/M/CommonUsage.dll differ
diff --git a/output/M/MedullaAdapter.dll b/output/M/MedullaAdapter.dll
index 059ddf8..34dc787 100644
Binary files a/output/M/MedullaAdapter.dll and b/output/M/MedullaAdapter.dll differ
diff --git a/output/M/MedullaAdapter.pdb b/output/M/MedullaAdapter.pdb
index 51a851a..1e174f5 100644
Binary files a/output/M/MedullaAdapter.pdb and b/output/M/MedullaAdapter.pdb differ
diff --git a/ref/CommonUsage.dll b/ref/CommonUsage.dll
index 128c1e7..cc330a2 100644
Binary files a/ref/CommonUsage.dll and b/ref/CommonUsage.dll differ