diff --git a/MultiWheelC/Experiments/NewControllerTrackingTests.cs b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
index ff48c25..559f71d 100644
--- a/MultiWheelC/Experiments/NewControllerTrackingTests.cs
+++ b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
@@ -47,7 +47,7 @@ namespace MultiWheelC
///
/// 获取或设置参考速度减速度,单位为m/s²。
///
- public double DecelerationMetersPerSecondSquared = 0.20;
+ public double DecelerationMetersPerSecondSquared = 0.12;
///
/// 获取或设置离散轨迹点间距,单位为m。
@@ -294,4 +294,314 @@ namespace MultiWheelC
MillimetersPerMeter));
}
}
+
+ ///
+ /// 从当前Detour位姿开始执行“3m直线—左半圆—3m直线”新版控制器跟踪实验。
+ ///
+ [MovementTest(name = "新版控制器:直线-左半圆-直线轨迹跟踪")]
+ public sealed class NewControllerStraightSemicircleStraightTest
+ : MovementTest
+ {
+ private const float MillimetersPerMeter = 1000f;
+
+ private readonly Painter _painter =
+ UI.GetPainter(
+ "NewControllerStraightSemicircleStraight");
+
+ private DriveTask _task;
+ private TrackingExperimentRecorder _recorder;
+ private DetourVehicleStateProvider _stateProvider;
+
+ ///
+ /// 获取或设置本次测试编号,用于区分重复实验CSV。
+ ///
+ public int TrialNumber = 1;
+
+ ///
+ /// 获取或设置半圆前后两段直线的长度,单位为m。
+ ///
+ public double StraightLengthMeters = 3.0;
+
+ ///
+ /// 获取或设置左转半圆的转弯半径,单位为m。
+ ///
+ public double TurnRadiusMeters = 2.0;
+
+ ///
+ /// 获取或设置直线与等曲率转弯之间的曲率过渡长度,单位为m。
+ ///
+ public double CurvatureTransitionLengthMeters = 0.60;
+
+ ///
+ /// 获取或设置两段直线的最大参考速度,单位为m/s。
+ ///
+ public double StraightMaximumSpeedMetersPerSecond = 0.30;
+
+ ///
+ /// 获取或设置半圆段的最大参考速度,单位为m/s。
+ ///
+ public double SemicircleMaximumSpeedMetersPerSecond = 0.25;
+
+ ///
+ /// 获取或设置参考速度加速度,单位为m/s²。
+ ///
+ public double AccelerationMetersPerSecondSquared = 0.20;
+
+ ///
+ /// 获取或设置参考速度减速度,单位为m/s²。
+ ///
+ public double DecelerationMetersPerSecondSquared = 0.12;
+
+ ///
+ /// 获取或设置离散轨迹点间距,单位为m。
+ ///
+ public double PointSpacingMeters = 0.02;
+
+ ///
+ /// 读取当前位姿、绘制组合轨迹并启动新版轨迹跟踪动作。
+ ///
+ public override void Test()
+ {
+ if (_task != null)
+ {
+ Console.WriteLine(
+ "新版直线-左半圆-直线测试已经在运行,请先停止当前测试。");
+ return;
+ }
+
+ if (!MovementTestPreparation.AreWheelsForward())
+ {
+ return;
+ }
+
+ var chassis =
+ PilotDefinition.Chassis as MultiWheelChassis;
+ if (chassis == null)
+ {
+ Console.WriteLine(
+ "当前底盘不是MultiWheelChassis,无法执行新版轨迹测试。");
+ return;
+ }
+
+ _stateProvider =
+ new DetourVehicleStateProvider();
+ if (!_stateProvider.TryGetState(
+ out var initialState))
+ {
+ Console.WriteLine(
+ "无法读取有效Detour起点位姿:" +
+ _stateProvider.LastFailureReason);
+ _stateProvider = null;
+ return;
+ }
+
+ var trajectory =
+ TestTrajectoryFactory
+ .CreateStraightLeftSemicircleStraight(
+ initialState.PoseInWorld,
+ StraightLengthMeters,
+ TurnRadiusMeters,
+ CurvatureTransitionLengthMeters,
+ StraightMaximumSpeedMetersPerSecond,
+ SemicircleMaximumSpeedMetersPerSecond,
+ AccelerationMetersPerSecondSquared,
+ DecelerationMetersPerSecondSquared,
+ PointSpacingMeters);
+
+ DrawTrajectory(trajectory);
+
+ var referenceStart = ToMillimeterVector(
+ trajectory.StartPoint.PoseInWorld);
+ var referenceEnd = ToMillimeterVector(
+ trajectory.EndPoint.PoseInWorld);
+ var recorder =
+ new TrackingExperimentRecorder(
+ controllerName: "NewStanleyPid",
+ trajectoryName:
+ "ProfiledStraightSmoothLeftTurnStraight",
+ trialNumber: TrialNumber,
+ referenceStart: referenceStart,
+ referenceEnd: referenceEnd,
+ referenceSpeed:
+ (float)StraightMaximumSpeedMetersPerSecond,
+ sampleIntervalMs: 50,
+ referenceAccelerationMetersPerSecondSquared:
+ (float)AccelerationMetersPerSecondSquared,
+ referenceDecelerationMetersPerSecondSquared:
+ (float)DecelerationMetersPerSecondSquared);
+ _recorder = recorder;
+
+ var controlPointRadiusMeters =
+ chassis.ControlPointRadius /
+ MillimetersPerMeter;
+ var movement =
+ new TrajectoryTrackingMovement
+ {
+ Trajectory = trajectory,
+ StateProvider = _stateProvider,
+ MaximumCommandSpeedMetersPerSecond =
+ StraightMaximumSpeedMetersPerSecond,
+ CycleObserver = controller =>
+ RecordControlCycle(
+ recorder,
+ controller,
+ controlPointRadiusMeters)
+ };
+
+ recorder.Start();
+
+ try
+ {
+ _task = new DriveTask(movement.Get());
+ _task.Wait();
+
+ // 保留少量停车后原始Detour数据,并生成一帧处理后的静止状态。
+ Thread.Sleep(400);
+ recorder.UpdateCommand(0f, 0f);
+ if (_stateProvider.TryGetState(
+ out var stoppedState))
+ {
+ recorder.UpdateProcessedState(
+ stoppedState);
+ }
+ }
+ finally
+ {
+ _task?.Stop();
+ recorder.UpdateCommand(0f, 0f);
+ recorder.StopAndSave();
+ _painter.Clear();
+ _task = null;
+ _recorder = null;
+ _stateProvider = null;
+ }
+ }
+
+ ///
+ /// 停止组合轨迹测试、保存已有数据并清除Clumsy轨迹可视化。
+ ///
+ public override void TestStop()
+ {
+ _task?.Stop();
+ _recorder?.UpdateCommand(0f, 0f);
+ _recorder?.StopAndSave();
+ _painter.Clear();
+ _task = null;
+ _recorder = null;
+ _stateProvider = null;
+ }
+
+ ///
+ /// 将组合轨迹的离散点和相邻线段转换为Clumsy毫米坐标后绘制。
+ ///
+ private void DrawTrajectory(
+ Trajectory.Trajectory2D trajectory)
+ {
+ _painter.Clear();
+
+ for (var index = 0;
+ index < trajectory.Count;
+ index++)
+ {
+ var point = ToMillimeterVector(
+ trajectory[index].PoseInWorld);
+ _painter.DrawDot(
+ Color.Cyan,
+ point.X,
+ point.Y,
+ 3f);
+
+ if (index == 0)
+ {
+ continue;
+ }
+
+ var previousPoint = ToMillimeterVector(
+ trajectory[index - 1].PoseInWorld);
+ _painter.DrawLine(
+ Color.DeepSkyBlue,
+ previousPoint.X,
+ previousPoint.Y,
+ point.X,
+ point.Y,
+ width: 2);
+ }
+
+ var start = ToMillimeterVector(
+ trajectory.StartPoint.PoseInWorld);
+ var end = ToMillimeterVector(
+ trajectory.EndPoint.PoseInWorld);
+ _painter.DrawDot(
+ Color.LimeGreen,
+ start.X,
+ start.Y,
+ 8f);
+ _painter.DrawDot(
+ Color.OrangeRed,
+ end.X,
+ end.Y,
+ 8f);
+ }
+
+ ///
+ /// 将组合轨迹控制周期的状态、参考量和最终GCP命令同步给实验记录器。
+ ///
+ private static void RecordControlCycle(
+ TrackingExperimentRecorder recorder,
+ ParkingGeometricController controller,
+ double controlPointRadiusMeters)
+ {
+ if (controller.LastVehicleState.HasValue)
+ {
+ recorder.UpdateProcessedState(
+ controller.LastVehicleState.Value);
+ }
+
+ if (!controller.LastCommand.HasValue)
+ {
+ return;
+ }
+
+ if (controller.LastProjection.HasValue &&
+ controller.LastReferenceSpeedMetersPerSecond.HasValue)
+ {
+ var projection =
+ controller.LastProjection.Value;
+ recorder.UpdateControlReference(
+ projection.ArcLengthMeters,
+ controller.LastReferenceSpeedMetersPerSecond.Value,
+ projection.LateralErrorMeters,
+ projection.HeadingErrorRadians,
+ projection.DistanceToTrajectoryMeters,
+ projection.RemainingDistanceMeters);
+ }
+
+ var command = controller.LastCommand.Value;
+ var curvaturePerMeter = Math.Tan(
+ command.FrontAngleRadians) /
+ controlPointRadiusMeters;
+ var angularSpeedRadiansPerSecond =
+ command.SpeedMetersPerSecond *
+ curvaturePerMeter;
+
+ recorder.UpdateCommand(
+ (float)command.SpeedMetersPerSecond,
+ (float)angularSpeedRadiansPerSecond);
+ }
+
+ ///
+ /// 将Shared世界坐标系米制位姿转换为Clumsy绘图和记录器使用的毫米坐标。
+ ///
+ private static Vector2 ToMillimeterVector(
+ Pose2D poseInWorld)
+ {
+ return new Vector2(
+ (float)(
+ poseInWorld.XMeters *
+ MillimetersPerMeter),
+ (float)(
+ poseInWorld.YMeters *
+ MillimetersPerMeter));
+ }
+ }
}
diff --git a/MultiWheelC/Experiments/TestTrajectoryFactory.cs b/MultiWheelC/Experiments/TestTrajectoryFactory.cs
index 4e7ea70..b215a0a 100644
--- a/MultiWheelC/Experiments/TestTrajectoryFactory.cs
+++ b/MultiWheelC/Experiments/TestTrajectoryFactory.cs
@@ -92,6 +92,399 @@ namespace MultiWheelC
return new Trajectory2D(points);
}
+ ///
+ /// 从当前位姿生成“3m直线、平滑进入半径2m左转、平滑退出、3m直线”的180°转弯轨迹。
+ ///
+ public static Trajectory2D CreateStraightLeftSemicircleStraight(
+ Pose2D startPoseInWorld,
+ double straightLengthMeters = 3.0,
+ double turnRadiusMeters = 2.0,
+ double curvatureTransitionLengthMeters = 0.60,
+ double straightMaximumSpeedMetersPerSecond = 0.30,
+ double semicircleMaximumSpeedMetersPerSecond = 0.25,
+ double accelerationMetersPerSecondSquared = 0.20,
+ double decelerationMetersPerSecondSquared = 0.12,
+ double pointSpacingMeters = 0.02)
+ {
+ EnsureFinitePose(
+ startPoseInWorld,
+ nameof(startPoseInWorld));
+ EnsureFinitePositive(
+ straightLengthMeters,
+ nameof(straightLengthMeters));
+ EnsureFinitePositive(
+ turnRadiusMeters,
+ nameof(turnRadiusMeters));
+ EnsureFinitePositive(
+ curvatureTransitionLengthMeters,
+ nameof(curvatureTransitionLengthMeters));
+ EnsureFinitePositive(
+ straightMaximumSpeedMetersPerSecond,
+ nameof(straightMaximumSpeedMetersPerSecond));
+ EnsureFinitePositive(
+ semicircleMaximumSpeedMetersPerSecond,
+ nameof(semicircleMaximumSpeedMetersPerSecond));
+ EnsureFinitePositive(
+ accelerationMetersPerSecondSquared,
+ nameof(accelerationMetersPerSecondSquared));
+ EnsureFinitePositive(
+ decelerationMetersPerSecondSquared,
+ nameof(decelerationMetersPerSecondSquared));
+ EnsureFinitePositive(
+ pointSpacingMeters,
+ nameof(pointSpacingMeters));
+
+ var originalSemicircleLengthMeters =
+ Math.PI * turnRadiusMeters;
+ var constantCurvatureLengthMeters =
+ originalSemicircleLengthMeters -
+ curvatureTransitionLengthMeters;
+
+ if (constantCurvatureLengthMeters <= 0.0)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(curvatureTransitionLengthMeters),
+ "曲率过渡段长度必须小于半径对应的原始半圆弧长。");
+ }
+
+ // 两段平滑过渡的平均曲率均为最大曲率的一半;
+ // 将等曲率段缩短一个过渡长度后,总曲率积分仍严格等于π。
+ var turnLengthMeters =
+ 2.0 * curvatureTransitionLengthMeters +
+ constantCurvatureLengthMeters;
+ var turnStartArcLengthMeters =
+ straightLengthMeters;
+ var turnEndArcLengthMeters =
+ straightLengthMeters +
+ turnLengthMeters;
+ var maximumCurvaturePerMeter =
+ 1.0 / turnRadiusMeters;
+
+ var sampleArcLengths =
+ BuildCompositeArcLengthSamples(
+ straightLengthMeters,
+ curvatureTransitionLengthMeters,
+ constantCurvatureLengthMeters,
+ pointSpacingMeters);
+ var speedLimits = new double[
+ sampleArcLengths.Count];
+ var referenceSpeeds = new double[
+ sampleArcLengths.Count];
+
+ for (var index = 0;
+ index < sampleArcLengths.Count;
+ index++)
+ {
+ var arcLengthMeters =
+ sampleArcLengths[index];
+
+ // 整个转弯及两侧曲率过渡段采用转弯限速。
+ speedLimits[index] =
+ arcLengthMeters >=
+ turnStartArcLengthMeters &&
+ arcLengthMeters <=
+ turnEndArcLengthMeters
+ ? semicircleMaximumSpeedMetersPerSecond
+ : straightMaximumSpeedMetersPerSecond;
+ }
+
+ ApplyAccelerationAndBrakingLimits(
+ sampleArcLengths,
+ speedLimits,
+ referenceSpeeds,
+ accelerationMetersPerSecondSquared,
+ decelerationMetersPerSecondSquared);
+
+ var points = new List(
+ sampleArcLengths.Count);
+ var startCos = Math.Cos(
+ startPoseInWorld.YawRadians);
+ var startSin = Math.Sin(
+ startPoseInWorld.YawRadians);
+ var localX = 0.0;
+ var localY = 0.0;
+ var localYawRadians = 0.0;
+ var previousArcLengthMeters = 0.0;
+
+ for (var index = 0;
+ index < sampleArcLengths.Count;
+ index++)
+ {
+ var arcLengthMeters =
+ sampleArcLengths[index];
+ if (index > 0)
+ {
+ var segmentLengthMeters =
+ arcLengthMeters -
+ previousArcLengthMeters;
+ var segmentMiddleArcLengthMeters =
+ (arcLengthMeters +
+ previousArcLengthMeters) /
+ 2.0;
+ var segmentCurvaturePerMeter =
+ CalculateSmoothTurnCurvature(
+ segmentMiddleArcLengthMeters -
+ turnStartArcLengthMeters,
+ curvatureTransitionLengthMeters,
+ constantCurvatureLengthMeters,
+ maximumCurvaturePerMeter);
+ var segmentYawChangeRadians =
+ segmentCurvaturePerMeter *
+ segmentLengthMeters;
+
+ if (Math.Abs(segmentCurvaturePerMeter) <=
+ 1e-12)
+ {
+ localX +=
+ Math.Cos(localYawRadians) *
+ segmentLengthMeters;
+ localY +=
+ Math.Sin(localYawRadians) *
+ segmentLengthMeters;
+ }
+ else
+ {
+ var nextYawRadians =
+ localYawRadians +
+ segmentYawChangeRadians;
+ localX +=
+ (Math.Sin(nextYawRadians) -
+ Math.Sin(localYawRadians)) /
+ segmentCurvaturePerMeter;
+ localY +=
+ (Math.Cos(localYawRadians) -
+ Math.Cos(nextYawRadians)) /
+ segmentCurvaturePerMeter;
+ }
+
+ localYawRadians +=
+ segmentYawChangeRadians;
+ }
+
+ var curvaturePerMeter =
+ CalculateSmoothTurnCurvature(
+ arcLengthMeters -
+ turnStartArcLengthMeters,
+ curvatureTransitionLengthMeters,
+ constantCurvatureLengthMeters,
+ maximumCurvaturePerMeter);
+
+ var worldX =
+ startPoseInWorld.XMeters +
+ startCos * localX -
+ startSin * localY;
+ var worldY =
+ startPoseInWorld.YMeters +
+ startSin * localX +
+ startCos * localY;
+ var worldYawRadians =
+ AngleMath.NormalizeRadians(
+ startPoseInWorld.YawRadians +
+ localYawRadians);
+
+ points.Add(
+ new TrajectoryPoint(
+ arcLengthMeters,
+ new Pose2D(
+ worldX,
+ worldY,
+ worldYawRadians),
+ curvaturePerMeter,
+ referenceSpeeds[index]));
+
+ previousArcLengthMeters =
+ arcLengthMeters;
+ }
+
+ return new Trajectory2D(points);
+ }
+
+ ///
+ /// 分别采样直线、入弯过渡、等曲率段和出弯过渡,保证所有边界均为精确轨迹点。
+ ///
+ private static List BuildCompositeArcLengthSamples(
+ double straightLengthMeters,
+ double curvatureTransitionLengthMeters,
+ double constantCurvatureLengthMeters,
+ double pointSpacingMeters)
+ {
+ var samples = new List { 0.0 };
+ var accumulatedArcLengthMeters = 0.0;
+
+ AppendSectionArcLengthSamples(
+ samples,
+ ref accumulatedArcLengthMeters,
+ straightLengthMeters,
+ pointSpacingMeters);
+ AppendSectionArcLengthSamples(
+ samples,
+ ref accumulatedArcLengthMeters,
+ curvatureTransitionLengthMeters,
+ pointSpacingMeters);
+ AppendSectionArcLengthSamples(
+ samples,
+ ref accumulatedArcLengthMeters,
+ constantCurvatureLengthMeters,
+ pointSpacingMeters);
+ AppendSectionArcLengthSamples(
+ samples,
+ ref accumulatedArcLengthMeters,
+ curvatureTransitionLengthMeters,
+ pointSpacingMeters);
+ AppendSectionArcLengthSamples(
+ samples,
+ ref accumulatedArcLengthMeters,
+ straightLengthMeters,
+ pointSpacingMeters);
+
+ return samples;
+ }
+
+ ///
+ /// 计算180°左转中连续变化的参考曲率,过渡段两端的曲率变化率均为零。
+ ///
+ private static double CalculateSmoothTurnCurvature(
+ double distanceInTurnMeters,
+ double transitionLengthMeters,
+ double constantCurvatureLengthMeters,
+ double maximumCurvaturePerMeter)
+ {
+ var totalTurnLengthMeters =
+ 2.0 * transitionLengthMeters +
+ constantCurvatureLengthMeters;
+
+ if (distanceInTurnMeters <= 0.0 ||
+ distanceInTurnMeters >= totalTurnLengthMeters)
+ {
+ return 0.0;
+ }
+
+ if (distanceInTurnMeters < transitionLengthMeters)
+ {
+ return maximumCurvaturePerMeter *
+ SmoothStep01(
+ distanceInTurnMeters /
+ transitionLengthMeters);
+ }
+
+ var exitTransitionStartMeters =
+ transitionLengthMeters +
+ constantCurvatureLengthMeters;
+ if (distanceInTurnMeters <=
+ exitTransitionStartMeters)
+ {
+ return maximumCurvaturePerMeter;
+ }
+
+ var exitRatio =
+ (distanceInTurnMeters -
+ exitTransitionStartMeters) /
+ transitionLengthMeters;
+ return maximumCurvaturePerMeter *
+ (1.0 - SmoothStep01(exitRatio));
+ }
+
+ ///
+ /// 将零到一的比例转换为两端一阶导数均为零的三次平滑比例。
+ ///
+ private static double SmoothStep01(double ratio)
+ {
+ var limitedRatio = Math.Max(
+ 0.0,
+ Math.Min(1.0, ratio));
+ return limitedRatio *
+ limitedRatio *
+ (3.0 - 2.0 * limitedRatio);
+ }
+
+ ///
+ /// 将一段指定长度的轨迹追加为均匀弧长采样,并使最后一个点严格落在该段终点。
+ ///
+ private static void AppendSectionArcLengthSamples(
+ ICollection samples,
+ ref double accumulatedArcLengthMeters,
+ double sectionLengthMeters,
+ double pointSpacingMeters)
+ {
+ var sectionStartArcLengthMeters =
+ accumulatedArcLengthMeters;
+ var segmentCount = (int)Math.Ceiling(
+ sectionLengthMeters /
+ pointSpacingMeters);
+
+ for (var index = 1;
+ index <= segmentCount;
+ index++)
+ {
+ samples.Add(
+ sectionStartArcLengthMeters +
+ sectionLengthMeters *
+ index /
+ segmentCount);
+ }
+
+ accumulatedArcLengthMeters =
+ sectionStartArcLengthMeters +
+ sectionLengthMeters;
+ }
+
+ ///
+ /// 对逐点速度上限执行前向加速约束和反向制动约束,生成连续可执行的空间速度曲线。
+ ///
+ private static void ApplyAccelerationAndBrakingLimits(
+ IReadOnlyList arcLengthsMeters,
+ IReadOnlyList speedLimitsMetersPerSecond,
+ double[] referenceSpeedsMetersPerSecond,
+ double accelerationMetersPerSecondSquared,
+ double decelerationMetersPerSecondSquared)
+ {
+ referenceSpeedsMetersPerSecond[0] = 0.0;
+
+ for (var index = 1;
+ index < arcLengthsMeters.Count;
+ index++)
+ {
+ var segmentLengthMeters =
+ arcLengthsMeters[index] -
+ arcLengthsMeters[index - 1];
+ var accelerationLimitedSpeed = Math.Sqrt(
+ referenceSpeedsMetersPerSecond[index - 1] *
+ referenceSpeedsMetersPerSecond[index - 1] +
+ 2.0 *
+ accelerationMetersPerSecondSquared *
+ segmentLengthMeters);
+
+ referenceSpeedsMetersPerSecond[index] =
+ Math.Min(
+ speedLimitsMetersPerSecond[index],
+ accelerationLimitedSpeed);
+ }
+
+ var finalIndex =
+ referenceSpeedsMetersPerSecond.Length - 1;
+ referenceSpeedsMetersPerSecond[finalIndex] = 0.0;
+
+ for (var index = finalIndex - 1;
+ index >= 0;
+ index--)
+ {
+ var segmentLengthMeters =
+ arcLengthsMeters[index + 1] -
+ arcLengthsMeters[index];
+ var brakingLimitedSpeed = Math.Sqrt(
+ referenceSpeedsMetersPerSecond[index + 1] *
+ referenceSpeedsMetersPerSecond[index + 1] +
+ 2.0 *
+ decelerationMetersPerSecondSquared *
+ segmentLengthMeters);
+
+ referenceSpeedsMetersPerSecond[index] =
+ Math.Min(
+ referenceSpeedsMetersPerSecond[index],
+ brakingLimitedSpeed);
+ }
+ }
+
///
/// 根据起步、巡航和制动能力计算指定弧长位置允许的参考速度。
///
diff --git a/MultiWheelC/Movements/TrajectoryTrackingMovement.cs b/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
index 5c7a140..dded045 100644
--- a/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
+++ b/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
@@ -38,7 +38,7 @@ namespace MultiWheelC
///
/// Stanley横向误差增益,单位为1/s。
///
- public double StanleyCrossTrackGainPerSecond = 1.0;
+ public double StanleyCrossTrackGainPerSecond = 0.4;
///
/// Stanley航向误差增益。
@@ -48,7 +48,7 @@ namespace MultiWheelC
///
/// Stanley低速分母保护速度,单位为m/s。
///
- public double StanleyMinimumSpeedMetersPerSecond = 0.05;
+ public double StanleyMinimumSpeedMetersPerSecond = 0.15;
///
/// 获取或设置Stanley是否优先使用Detour估算的实际速度。
diff --git a/MultiWheelC/PilotDefinition.cs b/MultiWheelC/PilotDefinition.cs
index 188fa94..a0b4551 100644
--- a/MultiWheelC/PilotDefinition.cs
+++ b/MultiWheelC/PilotDefinition.cs
@@ -33,10 +33,10 @@ public class PilotDefinition : MultiWheelPilotDefinition