diff --git a/MultiWheelC/Control/Execution/ParkingGeometricController.cs b/MultiWheelC/Control/Execution/ParkingGeometricController.cs
index 7c4f944..997c971 100644
--- a/MultiWheelC/Control/Execution/ParkingGeometricController.cs
+++ b/MultiWheelC/Control/Execution/ParkingGeometricController.cs
@@ -28,7 +28,7 @@ namespace MultiWheelC.Control.Execution
1e-6;
private const double StartupRegionMeters = 0.02;
private const double StartupPreviewDistanceMeters = 0.05;
- private const double MaximumStartupSpeedMetersPerSecond = 0.05;
+ private const double MaximumStartupSpeedMetersPerSecond = 0.08;
private readonly IVehicleStateProvider _stateProvider;
private readonly ILateralController _lateralController;
diff --git a/MultiWheelC/Control/Longitudinal/PidLongitudinalController.cs b/MultiWheelC/Control/Longitudinal/PidLongitudinalController.cs
index ab5f957..ecf61f0 100644
--- a/MultiWheelC/Control/Longitudinal/PidLongitudinalController.cs
+++ b/MultiWheelC/Control/Longitudinal/PidLongitudinalController.cs
@@ -23,11 +23,15 @@ namespace MultiWheelC.Control.Longitudinal
double integralGainPerSecond,
double derivativeGainSeconds,
double maximumIntegralCorrectionMetersPerSecond,
- double maximumCommandSpeedMetersPerSecond)
+ double maximumCommandSpeedMetersPerSecond,
+ double speedErrorDeadbandMetersPerSecond = 0.025)
{
EnsureFinitePositive(
maximumCommandSpeedMetersPerSecond,
nameof(maximumCommandSpeedMetersPerSecond));
+ EnsureFiniteNonNegative(
+ speedErrorDeadbandMetersPerSecond,
+ nameof(speedErrorDeadbandMetersPerSecond));
_feedbackPid = new PidController(
proportionalGain,
@@ -37,6 +41,8 @@ namespace MultiWheelC.Control.Longitudinal
derivativeOnMeasurement: true);
MaximumCommandSpeedMetersPerSecond =
maximumCommandSpeedMetersPerSecond;
+ SpeedErrorDeadbandMetersPerSecond =
+ speedErrorDeadbandMetersPerSecond;
}
///
@@ -49,6 +55,11 @@ namespace MultiWheelC.Control.Longitudinal
///
public double MaximumCommandSpeedMetersPerSecond { get; }
+ ///
+ /// 获取不触发纵向PID修正的速度误差死区,单位为m/s。
+ ///
+ public double SpeedErrorDeadbandMetersPerSecond { get; }
+
///
/// 获取最近一次有效控制周期的参考速度减实际速度,单位为m/s。
///
@@ -98,6 +109,20 @@ namespace MultiWheelC.Control.Longitudinal
referenceSpeedMetersPerSecond);
}
+ var speedErrorMetersPerSecond =
+ referenceSpeedMetersPerSecond -
+ context.ActualLongitudinalSpeedMetersPerSecond;
+
+ // Detour差分速度在参考速度附近会有小幅波动;死区内只使用速度前馈,
+ // 同时清除PID历史,避免噪声持续积累后产生突发修正。
+ if (Math.Abs(speedErrorMetersPerSecond) <=
+ SpeedErrorDeadbandMetersPerSecond)
+ {
+ Reset();
+ return LimitReferenceSpeed(
+ referenceSpeedMetersPerSecond);
+ }
+
GetCorrectionOutputRange(
referenceSpeedMetersPerSecond,
out var minimumCorrectionMetersPerSecond,
@@ -178,5 +203,22 @@ namespace MultiWheelC.Control.Longitudinal
"纵向控制器最大命令速度必须是正有限值。");
}
}
+
+ ///
+ /// 检查速度误差死区是否为非负有限值。
+ ///
+ private static void EnsureFiniteNonNegative(
+ double value,
+ string parameterName)
+ {
+ if (double.IsNaN(value) ||
+ double.IsInfinity(value) ||
+ value < 0.0)
+ {
+ throw new ArgumentOutOfRangeException(
+ parameterName,
+ "纵向控制器速度误差死区必须是非负有限值。");
+ }
+ }
}
}
diff --git a/MultiWheelC/Experiments/NewControllerTrackingTests.cs b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
index 559f71d..8c47315 100644
--- a/MultiWheelC/Experiments/NewControllerTrackingTests.cs
+++ b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
@@ -330,17 +330,17 @@ namespace MultiWheelC
///
/// 获取或设置直线与等曲率转弯之间的曲率过渡长度,单位为m。
///
- public double CurvatureTransitionLengthMeters = 0.60;
+ public double CurvatureTransitionLengthMeters = 0.70;
///
/// 获取或设置两段直线的最大参考速度,单位为m/s。
///
- public double StraightMaximumSpeedMetersPerSecond = 0.30;
+ public double StraightMaximumSpeedMetersPerSecond = 0.40;
///
/// 获取或设置半圆段的最大参考速度,单位为m/s。
///
- public double SemicircleMaximumSpeedMetersPerSecond = 0.25;
+ public double SemicircleMaximumSpeedMetersPerSecond = 0.30;
///
/// 获取或设置参考速度加速度,单位为m/s²。
diff --git a/MultiWheelC/Experiments/RotationTests.cs b/MultiWheelC/Experiments/RotationTests.cs
index 56f6b4e..08b3a45 100644
--- a/MultiWheelC/Experiments/RotationTests.cs
+++ b/MultiWheelC/Experiments/RotationTests.cs
@@ -1,6 +1,7 @@
using System;
+using System.Globalization;
using System.Numerics;
using System.Threading;
using ClumsyCore;
@@ -17,7 +18,6 @@ namespace MultiWheelC
public abstract class InPlaceRotateTestBase : MovementTest
{
public float RelativeAngleDegrees; // 相对当前航向的旋转角度,逆时针为正。
- public float MaxAngularSpeedDegreesPerSecond = 20f; // PID输出的最大角速度。
public int TrialNumber = 1; // 重复实验编号。
private DriveTask _task;
@@ -37,11 +37,18 @@ namespace MultiWheelC
// 从当前Detour航向开始,原地相对旋转指定角度并记录实验数据。
public override void Test()
{
+ var config = PilotDefinition.Conf;
+
if (float.IsNaN(RelativeAngleDegrees) ||
float.IsInfinity(RelativeAngleDegrees) ||
- float.IsNaN(MaxAngularSpeedDegreesPerSecond) ||
- float.IsInfinity(MaxAngularSpeedDegreesPerSecond) ||
- MaxAngularSpeedDegreesPerSecond <= 0f)
+ float.IsNaN(config.InPlaceRotateMaxSpeed) ||
+ float.IsInfinity(config.InPlaceRotateMaxSpeed) ||
+ config.InPlaceRotateMaxSpeed <= 0f ||
+ float.IsNaN(config.InPlaceRotateMinimumSpeed) ||
+ float.IsInfinity(config.InPlaceRotateMinimumSpeed) ||
+ config.InPlaceRotateMinimumSpeed <= 0f ||
+ config.InPlaceRotateMinimumSpeed >
+ config.InPlaceRotateMaxSpeed)
{
Console.WriteLine("原地旋转测试参数无效。");
return;
@@ -66,8 +73,25 @@ namespace MultiWheelC
(float)AngleMath.NormalizeDegrees(
location.th + RelativeAngleDegrees);
+ Console.WriteLine(
+ "原地自转实际参数:" +
+ $"Kp={config.InPlaceRotateKp:F3}," +
+ $"Ki={config.InPlaceRotateKi:F3}," +
+ $"Kd={config.InPlaceRotateKd:F3}," +
+ $"到位误差={config.InPlaceRotateArriveDeg:F2}°," +
+ $"最小角速度={config.InPlaceRotateMinimumSpeed:F2}°/s," +
+ $"最大角速度={config.InPlaceRotateMaxSpeed:F2}°/s," +
+ $"角加速度={config.InPlaceRotateAcc:F2}°/s²," +
+ $"舵轮到位误差={config.InPlaceRotateWheelAlignDeg:F2}°," +
+ $"旋转超时={config.InPlaceRotateTimeoutSec:F1}s;" +
+ $"起点航向={location.th:F2}°," +
+ $"目标航向={targetWorldAngle:F2}°。");
+ Console.WriteLine(
+ "原地自转CSV保存目录:" +
+ TrackingExperimentRecorder.DefaultOutputDirectory);
+
_recorder = new TrackingExperimentRecorder(
- controllerName: "InPlaceRotatePID",
+ controllerName: "InPlaceRotateFilteredPID",
trajectoryName: _trajectoryName,
trialNumber: TrialNumber,
referenceStart: rotationCenter,
@@ -75,7 +99,7 @@ namespace MultiWheelC
referenceSpeed: 0f,
referenceAngularSpeed:
(float)AngleMath.DegreesToRadians(
- MaxAngularSpeedDegreesPerSecond));
+ config.InPlaceRotateMaxSpeed));
_recorder.Start();
try
@@ -88,21 +112,26 @@ namespace MultiWheelC
PidparamsRead = () => new PIDParams
{
Kp =
- PilotDefinition.Conf.InPlaceRotateKp,
+ config.InPlaceRotateKp,
Ki =
- PilotDefinition.Conf.InPlaceRotateKi,
+ config.InPlaceRotateKi,
Kd =
- PilotDefinition.Conf.InPlaceRotateKd,
+ config.InPlaceRotateKd,
DeadZone =
- PilotDefinition.Conf
- .InPlaceRotateArriveDeg,
+ config.InPlaceRotateArriveDeg,
SpeedAccPerSec =
- PilotDefinition.Conf.InPlaceRotateAcc,
+ config.InPlaceRotateAcc,
OutputUpperThreshold =
- MaxAngularSpeedDegreesPerSecond,
+ config.InPlaceRotateMaxSpeed,
MaxI =
- PilotDefinition.Conf.InPlaceRotateMaxI
+ config.InPlaceRotateMaxI
},
+ MinimumAngularSpeedDegreesPerSecond =
+ config.InPlaceRotateMinimumSpeed,
+ WheelAlignmentToleranceDegrees =
+ config.InPlaceRotateWheelAlignDeg,
+ RotationTimeoutSeconds =
+ config.InPlaceRotateTimeoutSec,
CommandAngularSpeedObserver =
commandAngularSpeed =>
_recorder?.UpdateCommand(
@@ -136,23 +165,58 @@ namespace MultiWheelC
}
- [MovementTest(name = "SendXYThSpeed:原地自转90°")]
- public sealed class TestRotate90 :
+ [MovementTest(name = "SendXYThSpeed:输入角度原地自转")]
+ public sealed class TestRotateAngle :
InPlaceRotateTestBase
{
- public TestRotate90()
- : base(90f, "Rotate90")
+ public TestRotateAngle()
+ : base(0f, "RotateCustomAngle")
{
}
+
+ ///
+ /// 读取相对旋转角度并按正值逆时针、负值顺时针执行原地自转。
+ ///
+ public override void Test()
+ {
+ var input = UI.GetInput(
+ "输入相对旋转角度(deg,正数逆时针,负数顺时针,范围-180到180之间):");
+
+ if ((!float.TryParse(
+ input,
+ NumberStyles.Float,
+ CultureInfo.CurrentCulture,
+ out var relativeAngleDegrees) &&
+ !float.TryParse(
+ input,
+ NumberStyles.Float,
+ CultureInfo.InvariantCulture,
+ out relativeAngleDegrees)) ||
+ float.IsNaN(relativeAngleDegrees) ||
+ float.IsInfinity(relativeAngleDegrees))
+ {
+ Console.WriteLine("旋转角度输入无效,测试已经取消。");
+ return;
+ }
+
+ if (Math.Abs(relativeAngleDegrees) < 1e-3f)
+ {
+ Console.WriteLine("旋转角度不能为0,测试已经取消。");
+ return;
+ }
+
+ // 当前控制器按照圆周最短角旋转;精确±180°的方向存在二义性。
+ if (Math.Abs(relativeAngleDegrees) >= 180f)
+ {
+ Console.WriteLine(
+ "输入角度必须满足-180° < angle < 180°;" +
+ "当前最短角控制不支持指定精确±180°的旋转方向。");
+ return;
+ }
+
+ RelativeAngleDegrees = relativeAngleDegrees;
+ base.Test();
+ }
}
- [MovementTest(name = "SendXYThSpeed:原地自转180°")]
- public sealed class TestRotate180 :
- InPlaceRotateTestBase
- {
- public TestRotate180()
- : base(180f, "Rotate180")
- {
- }
- }
}
diff --git a/MultiWheelC/Experiments/TrackingExperimentRecorder.cs b/MultiWheelC/Experiments/TrackingExperimentRecorder.cs
index 1fd9ee3..a09ef46 100644
--- a/MultiWheelC/Experiments/TrackingExperimentRecorder.cs
+++ b/MultiWheelC/Experiments/TrackingExperimentRecorder.cs
@@ -145,6 +145,14 @@ namespace MultiWheelC
// 保存成功后的CSV绝对路径;尚未保存时为空。
public string SavedFilePath { get; private set; }
+ ///
+ /// 获取Clumsy当前运行目录下统一保存轨迹实验CSV的文件夹。
+ ///
+ public static string DefaultOutputDirectory =>
+ Path.Combine(
+ AppContext.BaseDirectory,
+ "TrackingExperiments");
+
// 启动后台采样线程。
public void Start()
{
@@ -242,6 +250,17 @@ namespace MultiWheelC
}
}
+ ///
+ /// 清除上一轨迹段参考量,避免停车或原地自转期间沿用已经结束的投影结果。
+ ///
+ public void ClearControlReference()
+ {
+ lock (_stateSyncRoot)
+ {
+ _hasControlReference = false;
+ }
+ }
+
// 停止采样并将本次实验保存为CSV;重复调用只保存一次。
public void StopAndSave()
{
@@ -444,9 +463,8 @@ namespace MultiWheelC
new List(_samples);
}
- var outputDirectory = Path.Combine(
- AppContext.BaseDirectory,
- "TrackingExperiments");
+ var outputDirectory =
+ DefaultOutputDirectory;
Directory.CreateDirectory(outputDirectory);
diff --git a/MultiWheelC/Movements/RotateInPlace.cs b/MultiWheelC/Movements/RotateInPlace.cs
index 883c87f..c35a517 100644
--- a/MultiWheelC/Movements/RotateInPlace.cs
+++ b/MultiWheelC/Movements/RotateInPlace.cs
@@ -7,6 +7,7 @@ using ClumsyCore.Pilot;
using CommonUsage.Chassis;
using MDCSToolBox.Commons.Controllers;
using MyParking.Shared;
+using MultiWheelC.StateEstimation;
namespace MultiWheelC
{
@@ -17,7 +18,11 @@ namespace MultiWheelC
///
public float AngleTarget;
- public Func ThetaReader = () => (float)DetourInterface.getCartLocation().th;
+ // 留作标定或单元测试时显式替换;为空时使用经过校验的Detour状态源。
+ public Func ThetaReader;
+
+ public IVehicleStateProvider StateProvider =
+ new DetourVehicleStateProvider();
public MultiWheelChassis Chassis = (MultiWheelChassis)PilotDefinition.Chassis;
@@ -37,6 +42,12 @@ namespace MultiWheelC
// 自转舵轮准备超时时间,单位s。
public float WheelAlignmentTimeoutSeconds = 10f;
+ // 航向尚未到位时允许下发的最小有效角速度,单位deg/s。
+ public float MinimumAngularSpeedDegreesPerSecond = 1f;
+
+ // 舵轮到位后执行航向闭环允许的最长时间,单位s。
+ public float RotationTimeoutSeconds = 15f;
+
// 先准备自转舵角,再通过安全版SendXYThSpeed闭环旋转到目标角度。
public override IEnumerable Get()
{
@@ -44,6 +55,8 @@ namespace MultiWheelC
throw new InvalidOperationException(
"当前底盘不是MultiWheelChassis,无法执行原地自转。");
+ ValidateParameters();
+
var adapter = new MultiWheelChassisAdapter(
Chassis,
PilotDefinition.Self.CarNum);
@@ -55,7 +68,9 @@ namespace MultiWheelC
DateTime? alignedSince = null;
while (true)
{
- if (!adapter.PrepareSpin())
+ if (!adapter.PrepareSpin(
+ alignmentToleranceDegrees:
+ WheelAlignmentToleranceDegrees))
throw new InvalidOperationException(
"无法生成原地自转舵轮目标:" +
adapter.LastFailureReason);
@@ -84,18 +99,86 @@ namespace MultiWheelC
yield return true;
}
+ var alignmentToleranceRadians =
+ AngleMath.DegreesToRadians(
+ WheelAlignmentToleranceDegrees);
+ if (!adapter.AdoptPreparedSpinForXYTh(
+ alignmentToleranceRadians))
+ {
+ throw new InvalidOperationException(
+ "无法将已到位的自转舵角交接给XYTh:" +
+ adapter.LastFailureReason);
+ }
+
var targetAngle =
(float)AngleMath.NormalizeDegrees(AngleTarget);
var p = PidparamsRead();
- thPid = new PIDController(ThetaReader, p.Kp);
+ var currentAngle = ReadCurrentAngleDegrees();
+ var cachedCurrentAngle = currentAngle;
+ thPid = new PIDController(
+ () => cachedCurrentAngle,
+ p.Kp);
thPid.ChangeParameters(p.Kp, p.Ki, p.Kd, p.MaxI, p.DeadZone,
p.OutputUpperThreshold, p.SpeedAccPerSec);
var lastCommandTime = DateTime.Now;
+ var rotationStarted = DateTime.Now;
while (true)
{
+ if ((DateTime.Now - rotationStarted)
+ .TotalSeconds >
+ RotationTimeoutSeconds)
+ {
+ throw new TimeoutException(
+ $"原地自转超过{RotationTimeoutSeconds:F1}s仍未到位。");
+ }
+
+ currentAngle = ReadCurrentAngleDegrees();
+ cachedCurrentAngle = currentAngle;
var s = thPid.GetResponse(targetAngle, true);
- Console.WriteLine($"s:{s} AngleTarget:{AngleTarget}");
+ var angleErrorDegrees =
+ (float)AngleMath
+ .ShortestDifferenceDegrees(
+ targetAngle,
+ currentAngle);
+
+ // PID进入到位死区后等待其0.3s稳定确认;等待期间
+ // 只清零驱动速度,不清除已经准备好的自转舵角状态。
+ if (Math.Abs(angleErrorDegrees) <=
+ p.DeadZone)
+ {
+ CommandAngularSpeedObserver?.Invoke(0f);
+ adapter
+ .StopXYThDrivePreserveSteeringState();
+
+ if (thPid.IsArrived())
+ break;
+
+ yield return true;
+ continue;
+ }
+
+ // PID输出低于底盘有效轮速范围时提高到最小可执行值,
+ // 避免接近目标时反复出现微小命令但车辆实际不动。
+ if (Math.Abs(s) > 1e-6f &&
+ Math.Abs(s) <
+ MinimumAngularSpeedDegreesPerSecond)
+ {
+ s = Math.Sign(angleErrorDegrees) *
+ MinimumAngularSpeedDegreesPerSecond;
+ }
+
+ // PID加速限制在首周期可能暂时输出零;此时保留
+ // 已交接的自转状态,等待下一周期产生有效角速度。
+ if (Math.Abs(s) <= 1e-6f)
+ {
+ CommandAngularSpeedObserver?.Invoke(0f);
+ adapter
+ .StopXYThDrivePreserveSteeringState();
+ yield return true;
+ continue;
+ }
+
CommandAngularSpeedObserver?.Invoke(s);
var now = DateTime.Now;
var interval = now - lastCommandTime;
@@ -118,7 +201,6 @@ namespace MultiWheelC
"安全XYTh原地旋转底盘解算失败:" +
adapter.LastFailureReason);
}
- if (thPid.IsArrived()) break;
yield return true;
}
@@ -130,5 +212,109 @@ namespace MultiWheelC
adapter.StopImmediately();
}
}
+
+ ///
+ /// 检查原地自转的舵轮准备、最小速度和超时参数是否可执行。
+ ///
+ private void ValidateParameters()
+ {
+ EnsureFinitePositive(
+ WheelAlignmentToleranceDegrees,
+ nameof(WheelAlignmentToleranceDegrees),
+ allowZero: true);
+ EnsureFinitePositive(
+ WheelAlignmentStableSeconds,
+ nameof(WheelAlignmentStableSeconds),
+ allowZero: true);
+ EnsureFinitePositive(
+ WheelAlignmentTimeoutSeconds,
+ nameof(WheelAlignmentTimeoutSeconds));
+ EnsureFinitePositive(
+ MinimumAngularSpeedDegreesPerSecond,
+ nameof(MinimumAngularSpeedDegreesPerSecond));
+ EnsureFinitePositive(
+ RotationTimeoutSeconds,
+ nameof(RotationTimeoutSeconds));
+
+ var pidParameters = PidparamsRead();
+ if (pidParameters == null)
+ {
+ throw new InvalidOperationException(
+ "原地自转PID参数读取结果为空。");
+ }
+
+ EnsureFinitePositive(
+ pidParameters.DeadZone,
+ "PidparamsRead.DeadZone");
+ EnsureFinitePositive(
+ pidParameters.OutputUpperThreshold,
+ "PidparamsRead.OutputUpperThreshold");
+ EnsureFinitePositive(
+ pidParameters.SpeedAccPerSec,
+ "PidparamsRead.SpeedAccPerSec");
+ EnsureFinitePositive(
+ pidParameters.Kp,
+ "PidparamsRead.Kp");
+
+ if (MinimumAngularSpeedDegreesPerSecond >
+ pidParameters.OutputUpperThreshold)
+ {
+ throw new InvalidOperationException(
+ "原地自转最小有效角速度不能大于最大角速度。");
+ }
+ }
+
+ ///
+ /// 读取经过状态源校验的世界航向,显式设置ThetaReader时优先使用替代读数。
+ ///
+ private float ReadCurrentAngleDegrees()
+ {
+ if (ThetaReader != null)
+ {
+ var angleDegrees = ThetaReader();
+ if (float.IsNaN(angleDegrees) ||
+ float.IsInfinity(angleDegrees))
+ {
+ throw new InvalidOperationException(
+ "自定义航向读取结果不是有效角度。");
+ }
+
+ return (float)AngleMath.NormalizeDegrees(
+ angleDegrees);
+ }
+
+ if (StateProvider == null ||
+ !StateProvider.TryGetState(out var state))
+ {
+ throw new InvalidOperationException(
+ "无法从Detour状态源读取有效车辆航向。" +
+ (StateProvider is DetourVehicleStateProvider provider
+ ? provider.LastFailureReason
+ : ""));
+ }
+
+ return (float)AngleMath.RadiansToDegrees(
+ state.PoseInWorld.YawRadians);
+ }
+
+ ///
+ /// 检查原地自转参数是否为正有限值,部分时间和容差参数允许为零。
+ ///
+ private static void EnsureFinitePositive(
+ float value,
+ string parameterName,
+ bool allowZero = false)
+ {
+ if (float.IsNaN(value) ||
+ float.IsInfinity(value) ||
+ (allowZero
+ ? value < 0f
+ : value <= 0f))
+ {
+ throw new ArgumentOutOfRangeException(
+ parameterName,
+ "原地自转参数必须是有效的正数。");
+ }
+ }
}
}
diff --git a/MultiWheelC/Movements/TrajectoryTrackingMovement.cs b/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
index dded045..553f6c7 100644
--- a/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
+++ b/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
@@ -75,6 +75,12 @@ namespace MultiWheelC
///
public double MaximumIntegralCorrectionMetersPerSecond = 0.05;
+ ///
+ /// 纵向PID不进行反馈修正的速度误差死区,单位为m/s。
+ ///
+ public double LongitudinalSpeedErrorDeadbandMetersPerSecond =
+ 0.025;
+
///
/// 底盘纵向命令速度绝对值上限,单位为m/s。
///
@@ -90,7 +96,7 @@ namespace MultiWheelC
/// 前后GCP目标转角最大变化率,单位为rad/s。
///
public double MaximumGcpAngleRateRadiansPerSecond =
- AngleMath.DegreesToRadians(10.0);
+ AngleMath.DegreesToRadians(15.0);
///
/// 终点位置和剩余弧长的完成容差,单位为m。
@@ -164,7 +170,8 @@ namespace MultiWheelC
LongitudinalKiPerSecond,
LongitudinalKdSeconds,
MaximumIntegralCorrectionMetersPerSecond,
- MaximumCommandSpeedMetersPerSecond);
+ MaximumCommandSpeedMetersPerSecond,
+ LongitudinalSpeedErrorDeadbandMetersPerSecond);
var gcpAllocator =
new AckermannGcpAllocator(
controlPointRadiusMeters,
diff --git a/MultiWheelC/PilotConfig.cs b/MultiWheelC/PilotConfig.cs
index 810f2f2..cff2ed1 100644
--- a/MultiWheelC/PilotConfig.cs
+++ b/MultiWheelC/PilotConfig.cs
@@ -26,7 +26,7 @@ public class PilotConfig : MultiWheelPilotConfig
public float InPlaceRotateSpeed = 30f;
[FieldMember(desc = "原地旋转:到位角度精度(deg)")]
- public float InPlaceRotateArriveDeg = 1f;
+ public float InPlaceRotateArriveDeg = 1.5f;
[FieldMember(desc = "原地旋转:起转前舵轮对齐精度(deg)")]
public float InPlaceRotateWheelAlignDeg = 2f;
@@ -38,24 +38,25 @@ public class PilotConfig : MultiWheelPilotConfig
#region 单车-临时
[FieldMember(desc = "原地旋转Kp")]
- public float InPlaceRotateKp = 0.2f;
- // public float InPlaceRotateKp = 0.2f;
+ public float InPlaceRotateKp = 1.1f;
[FieldMember(desc = "原地旋转Ki")]
- public float InPlaceRotateKi = 0.01f;
- // public float InPlaceRotateKi = 0.01f;
+ public float InPlaceRotateKi = 0f;
[FieldMember(desc = "原地旋转Kd")]
public float InPlaceRotateKd = 0f;
[FieldMember(desc = "原地旋转积分限幅")]
- public float InPlaceRotateMaxI = 0.01f;
+ public float InPlaceRotateMaxI = 0f;
+
+ [FieldMember(desc = "原地旋转最小有效角速度(deg/s)")]
+ public float InPlaceRotateMinimumSpeed = 1f;
[FieldMember(desc = "原地旋转最大角速度(deg/s)")]
- public float InPlaceRotateMaxSpeed = 30f;
+ public float InPlaceRotateMaxSpeed = 47.5f;
[FieldMember(desc = "原地旋转角加速度(deg/s²)")]
- public float InPlaceRotateAcc = 30f;
+ public float InPlaceRotateAcc = 60f;
[FieldMember(desc = "原地旋转超时(s)")]
public float InPlaceRotateTimeoutSec = 15f;
diff --git a/MultiWheelC/StateEstimation/DetourVehicleStateProvider.cs b/MultiWheelC/StateEstimation/DetourVehicleStateProvider.cs
index c94683f..4a07a9b 100644
--- a/MultiWheelC/StateEstimation/DetourVehicleStateProvider.cs
+++ b/MultiWheelC/StateEstimation/DetourVehicleStateProvider.cs
@@ -188,11 +188,11 @@ namespace MultiWheelC.StateEstimation
poseInWorld,
elapsedSeconds))
{
- state = AcceptPoseAfterVelocityRebase(
+ state = AcceptPoseAfterReset(
poseInWorld,
timestampSeconds);
LastFailureReason =
- "Detour位姿偏离上一速度预测,本次只更新位姿基准并保留滤波速度。";
+ "Detour位姿偏离速度预测,已重新建立速度估计基准。";
return true;
}
diff --git a/Shared/Chassis/MultiWheelChassisAdapter.cs b/Shared/Chassis/MultiWheelChassisAdapter.cs
index a23224f..5d2fbe2 100644
--- a/Shared/Chassis/MultiWheelChassisAdapter.cs
+++ b/Shared/Chassis/MultiWheelChassisAdapter.cs
@@ -504,13 +504,26 @@ namespace MyParking.Shared
/// 返回是否成功生成舵轮目标。
///
public bool PrepareSpin(
- TimeSpan? interval = null)
+ TimeSpan? interval = null,
+ double alignmentToleranceDegrees = 2.0)
{
+ ValidateFinite(
+ alignmentToleranceDegrees,
+ nameof(alignmentToleranceDegrees));
+
+ if (alignmentToleranceDegrees < 0.0)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(alignmentToleranceDegrees),
+ "自转舵轮到位容差必须是非负有限值。");
+ }
+
EnsureBodyFrameIsActive();
var success =
_chassis.PrepareRotateWheels(
- alignmentToleranceDegrees: 2.0f);
+ alignmentToleranceDegrees:
+ (float)alignmentToleranceDegrees);
if (!success)
{
diff --git a/data_process/新版控制器轨迹测试处理/plot_new_controller_experiment.py b/data_process/新版控制器轨迹测试处理/plot_new_controller_experiment.py
index a483532..7f37601 100644
--- a/data_process/新版控制器轨迹测试处理/plot_new_controller_experiment.py
+++ b/data_process/新版控制器轨迹测试处理/plot_new_controller_experiment.py
@@ -208,10 +208,13 @@ def load_experiment(csv_path: Path) -> dict[str, object]:
has_control_reference = (
numeric_column(frame, "HasControlReference", 0.0) > 0.5
)
- lateral_error = np.where(
- has_control_reference & np.isfinite(recorded_lateral_error),
- recorded_lateral_error,
- derived_lateral_error,
+ recorded_lateral_valid = (
+ has_control_reference & np.isfinite(recorded_lateral_error)
+ )
+ lateral_error = (
+ np.where(recorded_lateral_valid, recorded_lateral_error, np.nan)
+ if np.any(recorded_lateral_valid)
+ else derived_lateral_error
)
state_yaw = numeric_column(frame, "StateYawRadians")
@@ -230,10 +233,28 @@ def load_experiment(csv_path: Path) -> dict[str, object]:
frame,
"ControlHeadingErrorRadians",
)
- heading_error = np.where(
- has_control_reference & np.isfinite(recorded_heading_error),
- recorded_heading_error,
- derived_heading_error,
+ recorded_heading_valid = (
+ has_control_reference & np.isfinite(recorded_heading_error)
+ )
+ heading_error = (
+ np.where(recorded_heading_valid, recorded_heading_error, np.nan)
+ if np.any(recorded_heading_valid)
+ else derived_heading_error
+ )
+
+ # 投影定义满足:参考点 = 车体位置 + 横向误差 × 参考航向左法向。
+ # 因此无需假设轨迹类型,即可从有效控制周期还原车辆实际使用的参考轨迹。
+ projected_reference_yaw = actual_yaw + heading_error
+ reference_x = (
+ actual_x - lateral_error * np.sin(projected_reference_yaw)
+ )
+ reference_y = (
+ actual_y + lateral_error * np.cos(projected_reference_yaw)
+ )
+ valid_reference_position = (
+ has_control_reference
+ & np.isfinite(reference_x)
+ & np.isfinite(reference_y)
)
cruise_speed = first_finite(
@@ -270,6 +291,8 @@ def load_experiment(csv_path: Path) -> dict[str, object]:
numeric_column(frame, "ControlReferenceSpeedMetersPerSecond"),
ideal_speed,
)
+ if np.any(has_control_reference):
+ reference_speed[~has_control_reference] = np.nan
actual_speed = numeric_column(frame, "StateBodyVxMetersPerSecond")
velocity_valid = (
numeric_column(frame, "StateVelocityEstimateValid", 0.0) > 0.5
@@ -283,6 +306,9 @@ def load_experiment(csv_path: Path) -> dict[str, object]:
"actual_x": actual_x,
"actual_y": actual_y,
"valid_position": valid_position,
+ "reference_x": reference_x,
+ "reference_y": reference_y,
+ "valid_reference_position": valid_reference_position,
"start": start,
"end": end,
"length": length_meters,
@@ -336,13 +362,23 @@ def plot_experiment(
valid_position = data["valid_position"]
fig, axis = plt.subplots(figsize=(9.0, 6.5))
- axis.plot(
- [data["start"][0], data["end"][0]],
- [data["start"][1], data["end"][1]],
- "--",
- linewidth=2.0,
- label="期望4m直线轨迹",
- )
+ valid_reference_position = data["valid_reference_position"]
+ if np.count_nonzero(valid_reference_position) >= 2:
+ axis.plot(
+ data["reference_x"][valid_reference_position],
+ data["reference_y"][valid_reference_position],
+ "--",
+ linewidth=2.0,
+ label="控制器实际使用的参考轨迹",
+ )
+ else:
+ axis.plot(
+ [data["start"][0], data["end"][0]],
+ [data["start"][1], data["end"][1]],
+ "--",
+ linewidth=2.0,
+ label="参考起终点连线",
+ )
axis.plot(
data["actual_x"][valid_position],
data["actual_y"][valid_position],
@@ -451,7 +487,7 @@ def discover_csv_files(arguments: list[str]) -> list[Path]:
def main() -> None:
"""解析命令行并批量处理新版控制器实验CSV。"""
parser = argparse.ArgumentParser(
- description="绘制新版控制器4m直线实验的四类对比图。"
+ description="绘制新版控制器轨迹实验的四类对比图。"
)
parser.add_argument("csv", nargs="*", help="需要处理的CSV文件路径。")
parser.add_argument(
diff --git a/docs/clumsy参考.json b/docs/clumsy参考.json
new file mode 100644
index 0000000..f774adc
--- /dev/null
+++ b/docs/clumsy参考.json
@@ -0,0 +1,534 @@
+{
+ "layout": {
+ "chassis": {
+ "width": 1100.0,
+ "length": 1550.0,
+ "contour": [
+ -775.0,
+ 550.0,
+ 775.0,
+ 550.0,
+ 775.0,
+ -550.0,
+ -775.0,
+ -550.0
+ ]
+ },
+ "components": [
+ {
+ "type": "wheel",
+ "options": {
+ "platform": 0,
+ "scale": 1.0,
+ "radius": 200.0,
+ "group": null,
+ "id": 1,
+ "name": "w1",
+ "x": 0.0,
+ "y": 300.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "wheel",
+ "options": {
+ "platform": 6,
+ "scale": 1.0,
+ "radius": 200.0,
+ "group": null,
+ "id": 2,
+ "name": "w2",
+ "x": 0.0,
+ "y": -300.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "lidarssc",
+ "options": {
+ "usingLidars": "frontlidar",
+ "stopDist": 100.0,
+ "directionX": 1.0,
+ "directionY": 0.0,
+ "thresDot": 9999,
+ "contour": [
+ 0.0,
+ 0.0
+ ],
+ "group": [
+ "0",
+ "stop"
+ ],
+ "id": 411683697,
+ "name": "autoStop",
+ "x": 0.0,
+ "y": 0.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "lidarssc",
+ "options": {
+ "usingLidars": "frontlidar",
+ "stopDist": 100.0,
+ "directionX": 1.0,
+ "directionY": 0.0,
+ "thresDot": 999,
+ "contour": [
+ 0.0,
+ 0.0
+ ],
+ "group": [
+ "0",
+ "slow"
+ ],
+ "id": 726862610,
+ "name": "autoSlow",
+ "x": 0.0,
+ "y": 0.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "lidar2d",
+ "options": {
+ "isCircle": true,
+ "ignoreDist": 10.0,
+ "maxDist": 200000.0,
+ "useFilter": "",
+ "filterChassis": true,
+ "afterImageFilterOutN": 7,
+ "afterImageFilterOutDeg": 2.0,
+ "reflexThres": 0.4,
+ "reflexFilterWndSz": 30,
+ "reflexDistWnd": 50.0,
+ "reflexChunkThres": 2.5,
+ "BindLidar2dName": "",
+ "BindRelativeX": -4.9166203,
+ "BindRelativeY": 938.9871,
+ "BindRelativeTh": 1.300003,
+ "group": null,
+ "id": 1444795304,
+ "name": "rightlidar",
+ "x": -749.0,
+ "y": -475.0,
+ "yaw": 180.8,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "lidar2d",
+ "options": {
+ "isCircle": true,
+ "ignoreDist": 10.0,
+ "maxDist": 200000.0,
+ "useFilter": "",
+ "filterChassis": true,
+ "afterImageFilterOutN": 7,
+ "afterImageFilterOutDeg": 2.0,
+ "reflexThres": 0.4,
+ "reflexFilterWndSz": 30,
+ "reflexDistWnd": 50.0,
+ "reflexChunkThres": 2.5,
+ "BindLidar2dName": "",
+ "BindRelativeX": 0.0,
+ "BindRelativeY": 0.0,
+ "BindRelativeTh": 0.0,
+ "group": null,
+ "id": 1983955111,
+ "name": "leftlidar",
+ "x": -734.0,
+ "y": 475.0,
+ "yaw": 179.8,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "lidar3d",
+ "options": {
+ "ignoreDist": 5.0,
+ "maxDist": 200000.0,
+ "reduce": false,
+ "voxelSize": 70.0,
+ "pcklen": 82560,
+ "angleSgn": -1,
+ "endAngle": 0.0,
+ "RotationMatrix": [
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ -0.0,
+ 0.0,
+ 1.0
+ ],
+ "group": null,
+ "id": 1000069841,
+ "name": "frontlidar3d",
+ "x": 752.5,
+ "y": 0.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "plannar3dlidarzrange",
+ "options": {
+ "zmin": -65.0,
+ "zmax": 7.0,
+ "useAbsolute": true,
+ "samples": 1024,
+ "lidar3dName": "frontlidar3d",
+ "isCircle": true,
+ "ignoreDist": 10.0,
+ "maxDist": 200000.0,
+ "useFilter": "",
+ "filterChassis": true,
+ "afterImageFilterOutN": 7,
+ "afterImageFilterOutDeg": 2.0,
+ "reflexThres": 0.4,
+ "reflexFilterWndSz": 30,
+ "reflexDistWnd": 50.0,
+ "reflexChunkThres": 2.5,
+ "BindLidar2dName": "",
+ "BindRelativeX": 0.0,
+ "BindRelativeY": 0.0,
+ "BindRelativeTh": 0.0,
+ "group": null,
+ "id": 1954892242,
+ "name": "frontlidar",
+ "x": 752.5,
+ "y": 0.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "lidarssc",
+ "options": {
+ "usingLidars": "frontlidar",
+ "stopDist": 100.0,
+ "directionX": 1.0,
+ "directionY": 0.0,
+ "thresDot": 10,
+ "contour": [
+ 600.0,
+ -650.0,
+ 600.0,
+ 650.0,
+ 1200.0,
+ 650.0,
+ 1200.0,
+ -650.0
+ ],
+ "group": [
+ "1",
+ "stop"
+ ],
+ "id": 1519627219,
+ "name": "fstop1",
+ "x": 230.0,
+ "y": 0.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "lidarssc",
+ "options": {
+ "usingLidars": "frontlidar",
+ "stopDist": 100.0,
+ "directionX": 1.0,
+ "directionY": 0.0,
+ "thresDot": 10,
+ "contour": [
+ 1200.0,
+ -650.0,
+ 1200.0,
+ 650.0,
+ 2600.0,
+ 650.0,
+ 2600.0,
+ -650.0
+ ],
+ "group": [
+ "1",
+ "slow"
+ ],
+ "id": 771141297,
+ "name": "fslow1",
+ "x": 230.0,
+ "y": 0.0,
+ "yaw": 0.0,
+ "z": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ }
+ ]
+ },
+ "DriveTaskInterval": 30,
+ "DriveTaskTimeout": 9999.0,
+ "basicSpeed": 0.7,
+ "msConf": {
+ "SyncThAccPerSec": 5.0,
+ "TestCarSyncDistance": 2893.0,
+ "TestCarSyncTh": 0.0,
+ "ManualCarSyncVxFac": 0.3,
+ "ManualCarSyncVyFac": 1.0,
+ "ManualCarSyncVthFac": 30.0,
+ "MultiVehicleCrabSteerLimitDeg": 120.0,
+ "DeltaDetectCenter": 742.5,
+ "MultiVehicleSyncUseDetour": true,
+ "MultiVehicleManualUseDetourCorrection": true,
+ "MultiVehicleFleetNum": 2,
+ "MultiVehicleSyncInterval": 25,
+ "MultiVehicleMasterEndpoint": "/",
+ "SimpleIp": "192.168.1.101",
+ "MultiVehicleSelfEndpoint": "",
+ "MultiVehicleUseDetect": false,
+ "MultiVehicleControlRadius": 0.0,
+ "MultiVehicleAutoCmdTimeoutMs": 9999,
+ "MultiVehicleMemberTtlMs": 0,
+ "MultiVehicleAutoUseIdealCenter": true,
+ "MultiVehicleAutoRequireFleetCenter": true,
+ "MultiVehiclePosBiasXFac": 0.005,
+ "MultiVehiclePosBiasYFac": 0.15,
+ "MultiVehiclePosBiasThFac": 0.1,
+ "MultiVehiclePosBiasXThreshold": 0.05,
+ "MultiVehiclePosBiasYThreshold": 10.0,
+ "MultiVehiclePosBiasThThreshold": 5.0,
+ "MultiVehicleDetectBiasXFac": 0.0,
+ "MultiVehicleDetectBiasYFac": 0.0,
+ "MultiVehicleDetectBiasThFac": 0.0,
+ "MultiVehicleDetectBiasXThreshold": 0.0,
+ "MultiVehicleDetectBiasYThreshold": 0.0,
+ "MultiVehicleDetectBiasThThreshold": 0.0,
+ "MultiVehicleRotateCompXyFac": 0.003,
+ "MultiVehicleRotateCompXyIFac": 0.01,
+ "MultiVehicleRotateCompXyMax": 3.0,
+ "MultiVehicleRotateCompThFac": 0.1,
+ "MultiVehicleRotateCompThIFac": 0.01,
+ "MultiVehicleRotateCompThMax": 3.0,
+ "MultiVehicleRotateActiveOmega": 0.5,
+ "MultiVehicleRotateCompTangentFrac": 0.1,
+ "SingleCarSyncPrecisionXy": 10.0,
+ "SingleCarSyncPrecisionTh": 0.1,
+ "PlaygroundWebApiUrl": "http://localhost:18090",
+ "MultiVehicleRotatePoseWebApiDiagEnabled": false,
+ "PlaygroundRobotName": "agv_multi_1",
+ "PlaygroundNeighborRobotName": "agv_multi_2",
+ "WebApiTranslateMm": 100.0,
+ "WebApiRotateDeg": 5.0,
+ "InPlaceRotateTargetWorldDeg": 90.0,
+ "InPlaceRotateSpeed": 30.0,
+ "InPlaceRotateArriveDeg": 1.0,
+ "InPlaceRotateWheelAlignDeg": 2.0,
+ "InPlaceRotateActiveWheelAlignDeg": 10.0,
+ "FleetRotateOmega": 6.0,
+ "FleetRotateTargetDeltaDeg": 90.0,
+ "FleetRotateArriveDeg": 1.5,
+ "FleetRotateSlowDeg": 10.0,
+ "FleetRotateMinOmega": 0.5,
+ "FleetRotateAccel": 1.0,
+ "FleetRotateSettleSec": 0.5,
+ "FleetRotateUseDetourHeading": true,
+ "FleetCrabAngleDeg": 90.0,
+ "FleetCrabBodyWorldHeadingDeg": 0.0,
+ "FleetCrabLengthMm": 2000.0,
+ "FleetCrabSpeed": 0.35,
+ "FleetCrabAccel": 0.1,
+ "FleetCrabStartAccel": 0.02,
+ "FleetCrabSlowDistance": 800.0,
+ "FleetCrabFinishDistance": 10.0,
+ "FleetCrabFinishSpeed": 0.0,
+ "FleetCrabSlowingPow": 0.7,
+ "FleetCrabGcpThetaThreshold": 120.0,
+ "FleetCrabDthLinearFac": 3.3,
+ "FleetCrabDthLinearThreshold": 10.0,
+ "FleetCrabStartSyncTimeoutSec": 9999.0,
+ "FleetCrabStartWheelAlignDeg": 2.0,
+ "TwoLegLidarName": "leftlidar,rearlidar",
+ "TwoLegGuessX": -2000.0,
+ "TwoLegWidth": 450.0,
+ "TwoLegWidthErr": 50.0,
+ "TwoLegBlobDist": 100.0,
+ "TwoLegBlobSize": 200.0,
+ "TwoLegBlobPtCount": 10,
+ "TwoLegPadding": 5,
+ "TwoLegPillarFindingScope": 20,
+ "TwoLegSgnDir": 1,
+ "TwoLegCenterChangeX": 0.0,
+ "TwoLegOutputBiasX": -15.0,
+ "TwoLegOutputBiasY": 0.0,
+ "TwoLegFilterLength": 500.0,
+ "TwoLegFilterWidth": 800.0,
+ "TireFilterLength": 1000.0,
+ "TireFilterWidth": 1900.0,
+ "TireTwoLegWidth": 1470.0,
+ "TireTwoLegWidthErr": 200.0,
+ "TireTwoLegBlobPtCount": 10,
+ "TireFrontTwoLegBlobDist": 100.0,
+ "TireFrontTwoLegBlobSize": 200.0,
+ "TireFrontPadding": 10,
+ "TireFrontTwoLegPillarFindingScope": 10,
+ "TireFrontTwoLegSgnDir": 1,
+ "TireFrontTwoLegCenterChangeX": 0.0,
+ "TireBackTwoLegBlobDist": 100.0,
+ "TireBackTwoLegBlobSize": 200.0,
+ "TireBackPadding": 5,
+ "TireBackTwoLegPillarFindingScope": 20,
+ "TireBackTwoLegSgnDir": 1,
+ "TireBackTwoLegCenterChangeX": 0.0,
+ "ClampControlKp": 0.0015,
+ "ClampControlKi": 0.0,
+ "ClampControlKd": 0.0,
+ "ClampControlMaxI": 0.02,
+ "ClampControlSpeedAcc": 2.0,
+ "ClampControlThresh": 0.2,
+ "ClampControlDeadZone": 30.0,
+ "MaxClampSpeed": 12.0,
+ "LineTrackDistance": 1000.0,
+ "LineTrackMaxSpeed": 0.3,
+ "LineTrackKp": 0.001,
+ "LineTrackKi": 0.0,
+ "LineTrackKd": 0.0,
+ "LineTrackDeadZone": 10.0,
+ "TireFollowingWalkBlindSwitchingDistance": 1300.0,
+ "TireFollowingStage1GuessX": 2200.0,
+ "TireFollowingStage2GuessX": 2600.0,
+ "TireFollowingWalkBlindFinishDistance": 10.0,
+ "TireFollowingSlowDistance": 750.0,
+ "TireFollowingMaxSpeed": 0.3,
+ "TireFollowingFrontLidarPathTransformationX": 160.0,
+ "TireFollowingFrontLidarPathTransformationY": 3.0,
+ "TireFollowingFrontLidarWalkBlindTh": 0.0,
+ "TireFollowingBackLidarPathTransformationX": 155.0,
+ "TireFollowingBackLidarPathTransformationY": 0.0,
+ "TireFollowingBackLidarWalkBlindTh": 0.0,
+ "TireFollowingLeaveCarBackLidarPathTransformationX": 1700.0,
+ "TireFollowingLeaveCarWalkBlindSwitchingDistance": 2400.0,
+ "TireFollowingTireNum": 2,
+ "TireFollowingCloseDistance": 1400.0,
+ "TireFollowingAngleIgnoreThr": 1.0,
+ "TireFollowingYAverageFrameCount": 6,
+ "DstTrackerMaxSpeed": 0.3,
+ "GcpThetaThreshold": 70.0,
+ "DthLinearFac": 0.75,
+ "DthLinearThreshold": 25.0,
+ "BiasFac": 0.5,
+ "BiasThreshold": 15.0,
+ "BiasControlGainFac": 1.0,
+ "BiasSlowSigma": 55.0,
+ "LineMagKp": 0.1,
+ "LineMagKi": 0.0,
+ "LineMagKd": 0.1,
+ "MagMaxI": 10.0,
+ "MagDeadZone": 1.0,
+ "LineMagThresh": 35.0,
+ "CurveMagKp": 0.4,
+ "CurveMagKi": 0.0,
+ "CurveMagKd": 0.1,
+ "CurveMagThresh": 65.0,
+ "MotionDebugPrint": true,
+ "DebugCurvature": false,
+ "SlowDistance": 1000.0,
+ "SlowingPow": 0.8,
+ "FinishDistance": 5.0,
+ "FinishSpeed": 0.02,
+ "FirstThAccuracy": 2.0,
+ "ThContinuousThreshold": 10.0,
+ "FirstRotateSpeedFac": 1.0,
+ "FirstRotateMaxSpeed": 30.0,
+ "FirstRotateAcc": 20.0,
+ "FirstRotateDeAcc": 30.0,
+ "SpeedAccPerSecond": 0.1,
+ "SpeedDeAccPerSecond": 1.0,
+ "NotContinuousAngle": 3.0,
+ "PowerSteeringLookAhead": 100.0,
+ "SpeedLookAhead": 1500.0,
+ "SpeedLookAheadCurveDiff": 1000.0,
+ "SpeedLookBackCurveDiff": 200.0,
+ "SpeedLimitCurveDiffMin": 0.2,
+ "SpeedLimitCurveMin": 0.2,
+ "MaxRotateSpeedCurveLimit": 30.0,
+ "MaxRotateAccCurveLimit": 30.0,
+ "BaisAlarmValue": 1500.0,
+ "DthAlarmValue": 150.0,
+ "UseAutoAvoidance": false,
+ "ObstacleStopDistance": 1000.0,
+ "ObstacleSlowDistance": 2500.0,
+ "CoefficientOfExpansion": 1.0,
+ "TargetSpeed": 0.5,
+ "EmptyCartLength": 1550.0,
+ "EmptyCartWidth": 1100.0,
+ "RotateStopFac": 1.3,
+ "RotateSlowFac": 1.8,
+ "SlowPow": 1.2,
+ "ShieldAutoObstacle": false,
+ "LidarName": "frontlidar",
+ "ObstacleRecoveryTime": 500,
+ "UseCameraAvoidance": false,
+ "UseManualContorolAvoidance": true,
+ "ManualAutoAvoidcaneSlowDistance": 500.0,
+ "ManualAutoAvoidcaneStopDistance": 300.0,
+ "ShieldAutoAvoidance": false,
+ "UpCamPoseX": 0.0,
+ "UpCamPoseY": 0.0,
+ "UpCamPoseTh": 0.0,
+ "DownCamPoseX": 0.0,
+ "DownCamPoseY": 0.0,
+ "DownCamPoseTh": 0.0,
+ "OutMapEnable": true,
+ "GroundLossThreshold": 5.0,
+ "LaserLossThreshold": 15.0,
+ "RiskSlowdownThreshold": 15.0,
+ "UseSimpleDetector": false,
+ "LoseConnectionTime": 5000,
+ "GroundCameraDisconnectAlarmTime": 200,
+ "FrontLidarName": "null",
+ "RearLidarName": "null",
+ "UseSkidDetector": true,
+ "SkidTimeThreshold": 3000,
+ "SkidFacThreshold": 3.0,
+ "MissionWarningTime": 3000,
+ "UseGyrosDetector": false,
+ "GyrosErrorTime": 3000,
+ "GyrosErrorFac": 10,
+ "ObstacleStopDec": 0.1
+ },
+ "script": "MultiWheelC.dll",
+ "guru": {
+ "MaxLogFiles": 20,
+ "interpreter": "javascript",
+ "throwSAIError": true
+ },
+ "locationTimeout": 100,
+ "IOCheckIntegrity": true,
+ "detourHost": "127.0.0.1",
+ "detourPort": 4321
+}
\ No newline at end of file