diff --git a/MedullaAdapter/build/Medulla/plugins/CommonUsage.dll b/MedullaAdapter/build/Medulla/plugins/CommonUsage.dll
index 82a8d1d..b4d199e 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 b225c77..0fca26f 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 f755db3..246e2b6 100644
Binary files a/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.pdb and b/MedullaAdapter/build/Medulla/plugins/MedullaAdapter.pdb differ
diff --git a/MultiWheelC/Control/Execution/ParkingGeometricController.cs b/MultiWheelC/Control/Execution/ParkingGeometricController.cs
index b17c559..d8f6be4 100644
--- a/MultiWheelC/Control/Execution/ParkingGeometricController.cs
+++ b/MultiWheelC/Control/Execution/ParkingGeometricController.cs
@@ -51,7 +51,8 @@ namespace MultiWheelC.Control.Execution
double finishSpeedMetersPerSecond = 0.02,
double finishHeadingToleranceRadians =
3.0 * Math.PI / 180.0,
- double maximumDistanceToTrajectoryMeters = 0.30)
+ double maximumDistanceToTrajectoryMeters = 0.30,
+ double terminalBrakingPreviewMeters = 0.02)
{
_stateProvider = stateProvider ??
throw new ArgumentNullException(
@@ -81,6 +82,9 @@ namespace MultiWheelC.Control.Execution
EnsureFinitePositive(
maximumDistanceToTrajectoryMeters,
nameof(maximumDistanceToTrajectoryMeters));
+ EnsureFiniteNonNegative(
+ terminalBrakingPreviewMeters,
+ nameof(terminalBrakingPreviewMeters));
FinishDistanceMeters = finishDistanceMeters;
FinishSpeedMetersPerSecond =
@@ -89,6 +93,8 @@ namespace MultiWheelC.Control.Execution
finishHeadingToleranceRadians;
MaximumDistanceToTrajectoryMeters =
maximumDistanceToTrajectoryMeters;
+ TerminalBrakingPreviewMeters =
+ terminalBrakingPreviewMeters;
}
///
@@ -111,6 +117,11 @@ namespace MultiWheelC.Control.Execution
///
public double MaximumDistanceToTrajectoryMeters { get; }
+ ///
+ /// 获取沿轨迹提前读取更低制动参考速度的距离,单位为m。
+ ///
+ public double TerminalBrakingPreviewMeters { get; }
+
///
/// 获取控制器当前是否持有并正在执行一条轨迹。
///
@@ -288,7 +299,7 @@ namespace MultiWheelC.Control.Execution
}
///
- /// 在轨迹起点零速固定点处读取前方速度,并限制为低速起步命令。
+ /// 在轨迹起步区域内保持最低释放速度,避免空间速度曲线零速固定点。
///
private double ResolveReferenceSpeedForControl(
TrajectoryProjection projection)
@@ -297,15 +308,18 @@ namespace MultiWheelC.Control.Execution
projection.ReferencePoint
.ReferenceSpeedMetersPerSecond;
- var requiresStartupRelease =
+ currentReferenceSpeed =
+ ApplyTerminalBrakingPreview(
+ projection,
+ currentReferenceSpeed);
+
+ var isInStartupRegion =
projection.ArcLengthMeters <=
StartupRegionMeters &&
projection.RemainingDistanceMeters >
- FinishDistanceMeters &&
- Math.Abs(currentReferenceSpeed) <=
- ZeroReferenceSpeedToleranceMetersPerSecond;
+ FinishDistanceMeters;
- if (!requiresStartupRelease)
+ if (!isInStartupRegion)
{
return currentReferenceSpeed;
}
@@ -314,6 +328,7 @@ namespace MultiWheelC.Control.Execution
_trajectory.TotalLengthMeters,
projection.ArcLengthMeters +
StartupPreviewDistanceMeters);
+
var previewReferenceSpeed =
_trajectory
.SampleAtArcLength(
@@ -323,13 +338,67 @@ namespace MultiWheelC.Control.Execution
if (Math.Abs(previewReferenceSpeed) <=
ZeroReferenceSpeedToleranceMetersPerSecond)
{
- return 0.0;
+ return currentReferenceSpeed;
}
- return Math.Sign(previewReferenceSpeed) *
- Math.Min(
- Math.Abs(previewReferenceSpeed),
- MaximumStartupSpeedMetersPerSecond);
+ var startupReleaseSpeed =
+ Math.Sign(previewReferenceSpeed) *
+ Math.Min(
+ Math.Abs(previewReferenceSpeed),
+ MaximumStartupSpeedMetersPerSecond);
+
+ // 当前空间速度已经高于起步释放速度时,
+ // 正常交还给原始速度曲线。
+ if (Math.Sign(currentReferenceSpeed) ==
+ Math.Sign(startupReleaseSpeed) &&
+ Math.Abs(currentReferenceSpeed) >=
+ Math.Abs(startupReleaseSpeed))
+ {
+ return currentReferenceSpeed;
+ }
+
+ return startupReleaseSpeed;
+ }
+
+ ///
+ /// 采用前方更低的同方向参考速度,使车辆在终点减速段提前制动。
+ ///
+ private double ApplyTerminalBrakingPreview(
+ TrajectoryProjection projection,
+ double currentReferenceSpeed)
+ {
+ if (TerminalBrakingPreviewMeters <= 0.0)
+ {
+ return currentReferenceSpeed;
+ }
+
+ var previewArcLengthMeters = Math.Min(
+ _trajectory.TotalLengthMeters,
+ projection.ArcLengthMeters +
+ TerminalBrakingPreviewMeters);
+ var previewReferenceSpeed =
+ _trajectory
+ .SampleAtArcLength(
+ previewArcLengthMeters)
+ .ReferenceSpeedMetersPerSecond;
+
+ var previewIsStop =
+ Math.Abs(previewReferenceSpeed) <=
+ ZeroReferenceSpeedToleranceMetersPerSecond;
+ var hasSameDirection =
+ Math.Sign(previewReferenceSpeed) ==
+ Math.Sign(currentReferenceSpeed);
+ var previewIsSlower =
+ Math.Abs(previewReferenceSpeed) <
+ Math.Abs(currentReferenceSpeed);
+
+ if (previewIsSlower &&
+ (previewIsStop || hasSameDirection))
+ {
+ return previewReferenceSpeed;
+ }
+
+ return currentReferenceSpeed;
}
///
diff --git a/MultiWheelC/Experiments/CompositeMotionPlanTests.cs b/MultiWheelC/Experiments/CompositeMotionPlanTests.cs
index 715b5e8..cb612d9 100644
--- a/MultiWheelC/Experiments/CompositeMotionPlanTests.cs
+++ b/MultiWheelC/Experiments/CompositeMotionPlanTests.cs
@@ -37,7 +37,7 @@ namespace MultiWheelC
public double StraightMaximumSpeedMetersPerSecond = 0.40; // 直线限速。
public double CurveMaximumSpeedMetersPerSecond = 0.30; // 转弯和过渡段限速。
public double AccelerationMetersPerSecondSquared = 0.20; // 参考加速度。
- public double DecelerationMetersPerSecondSquared = 0.12; // 参考减速度。
+ public double DecelerationMetersPerSecondSquared = 0.08; // 参考减速度。
public double PointSpacingMeters = 0.02; // 离散轨迹点间距。
///
diff --git a/MultiWheelC/Experiments/NewControllerTrackingTests.cs b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
index ceab046..0c8371e 100644
--- a/MultiWheelC/Experiments/NewControllerTrackingTests.cs
+++ b/MultiWheelC/Experiments/NewControllerTrackingTests.cs
@@ -124,7 +124,7 @@ namespace MultiWheelC
///
/// 获取或设置参考速度减速度,单位为m/s²。
///
- public double DecelerationMetersPerSecondSquared = 0.10;
+ public double DecelerationMetersPerSecondSquared = 0.08;
///
/// 获取或设置离散轨迹点间距,单位为m。
@@ -485,7 +485,7 @@ namespace MultiWheelC
///
/// 获取或设置参考速度减速度,单位为m/s²。
///
- public double DecelerationMetersPerSecondSquared = 0.12;
+ public double DecelerationMetersPerSecondSquared = 0.08;
///
/// 获取或设置离散轨迹点间距,单位为m。
diff --git a/MultiWheelC/Movements/TrajectoryTrackingMovement.cs b/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
index ad6f2cb..a69d0e7 100644
--- a/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
+++ b/MultiWheelC/Movements/TrajectoryTrackingMovement.cs
@@ -126,6 +126,11 @@ namespace MultiWheelC
public double FinishHeadingToleranceRadians =
AngleMath.DegreesToRadians(3.0);
+ ///
+ /// 终点减速阶段提前读取参考速度的距离,单位为m。
+ ///
+ public double TerminalBrakingPreviewMeters = 0.02;
+
///
/// 车辆允许偏离参考轨迹的最大欧氏距离,单位为m。
///
@@ -203,7 +208,8 @@ namespace MultiWheelC
FinishDistanceMeters,
FinishSpeedMetersPerSecond,
FinishHeadingToleranceRadians,
- MaximumDistanceToTrajectoryMeters);
+ MaximumDistanceToTrajectoryMeters,
+ TerminalBrakingPreviewMeters);
var clock = Stopwatch.StartNew();
var previousCycleSeconds =
diff --git a/MultiWheelC/StateEstimation/WheelFeedbackVehicleStateProvider.cs b/MultiWheelC/StateEstimation/WheelFeedbackVehicleStateProvider.cs
new file mode 100644
index 0000000..77537a5
--- /dev/null
+++ b/MultiWheelC/StateEstimation/WheelFeedbackVehicleStateProvider.cs
@@ -0,0 +1,274 @@
+using System;
+using CommonUsage.Chassis;
+using MyParking.Shared;
+using System.Diagnostics;
+
+namespace MultiWheelC.StateEstimation
+{
+ ///
+ /// 保留外部状态源的Detour位姿,并以舵轮电机反馈解算的车体纵向速度替换Detour差分纵向速度。
+ ///
+ public sealed class WheelFeedbackVehicleStateProvider
+ : IVehicleStateProvider
+ {
+ private readonly Stopwatch _wheelSpeedClock = Stopwatch.StartNew();
+ public const double DefaultVelocityFilterTimeConstantSeconds =
+ 0.10;
+
+ private readonly object _syncRoot = new object();
+ private readonly IVehicleStateProvider _poseProvider;
+ private readonly MultiWheelChassis _chassis;
+ private readonly FirstOrderLowPassFilter _longitudinalSpeedFilter;
+
+ private bool _hasPreviousTimestamp;
+ private double _previousTimestampSeconds;
+ private bool _hasVelocityDiagnostics;
+ private double _latestDetourBodyVxMetersPerSecond;
+ private bool _latestDetourVelocityValid;
+ private double _latestRawWheelBodyVxMetersPerSecond;
+ private double _latestFilteredWheelBodyVxMetersPerSecond;
+ private bool _latestWheelVelocityValid;
+
+ ///
+ /// 创建使用默认0.10s低通时间常数的电机反馈纵向速度状态源。
+ ///
+ public WheelFeedbackVehicleStateProvider(
+ IVehicleStateProvider poseProvider,
+ MultiWheelChassis chassis)
+ : this(
+ poseProvider,
+ chassis,
+ DefaultVelocityFilterTimeConstantSeconds)
+ {
+ }
+
+ ///
+ /// 创建使用指定低通时间常数的电机反馈纵向速度状态源。
+ ///
+ public WheelFeedbackVehicleStateProvider(
+ IVehicleStateProvider poseProvider,
+ MultiWheelChassis chassis,
+ double velocityFilterTimeConstantSeconds)
+ {
+ _poseProvider = poseProvider ??
+ throw new ArgumentNullException(
+ nameof(poseProvider));
+ _chassis = chassis ??
+ throw new ArgumentNullException(
+ nameof(chassis));
+ _longitudinalSpeedFilter =
+ new FirstOrderLowPassFilter(
+ velocityFilterTimeConstantSeconds);
+ }
+
+ ///
+ /// 获取最近一次读取失败的原因,正常时为空字符串。
+ ///
+ public string LastFailureReason { get; private set; } =
+ string.Empty;
+
+ ///
+ /// 读取Detour位姿和电机反馈速度,并组合成统一车辆状态。
+ ///
+ public bool TryGetState(out VehicleState state)
+ {
+ lock (_syncRoot)
+ {
+ if (!_poseProvider.TryGetState(
+ out var poseState))
+ {
+ state = default;
+ LastFailureReason =
+ "基础位姿状态源暂时不可用。";
+ return false;
+ }
+
+ try
+ {
+ var actualCarSpeed =
+ _chassis.GetCarSpeed(true);
+ var rawLongitudinalSpeedMetersPerSecond =
+ (double)actualCarSpeed.Vx;
+
+ EnsureFinite(
+ rawLongitudinalSpeedMetersPerSecond,
+ "电机反馈车体纵向速度");
+
+ var wheelSpeedTimestampSeconds =
+ _wheelSpeedClock.Elapsed.TotalSeconds;
+
+ var filteredLongitudinalSpeedMetersPerSecond =
+ UpdateLongitudinalSpeedFilter(
+ rawLongitudinalSpeedMetersPerSecond,
+ wheelSpeedTimestampSeconds,
+ out var hasValidWheelSpeedEstimate);
+
+ _latestDetourBodyVxMetersPerSecond =
+ poseState.TwistInBody.VxMetersPerSecond;
+ _latestDetourVelocityValid =
+ poseState.HasValidVelocityEstimate;
+ _latestRawWheelBodyVxMetersPerSecond =
+ rawLongitudinalSpeedMetersPerSecond;
+ _latestFilteredWheelBodyVxMetersPerSecond =
+ filteredLongitudinalSpeedMetersPerSecond;
+ _latestWheelVelocityValid =
+ hasValidWheelSpeedEstimate;
+ _hasVelocityDiagnostics = true;
+
+ // 第一阶段只替换控制器使用的车体纵向速度;横向速度和角速度
+ // 继续使用Detour估计,避免轮速差和舵角误差放大Vy与Omega噪声。
+ var twistInBody = new Twist2D(
+ filteredLongitudinalSpeedMetersPerSecond,
+ poseState.TwistInBody
+ .VyMetersPerSecond,
+ poseState.TwistInBody
+ .OmegaRadiansPerSecond);
+
+ var twistInWorld =
+ FrameTransform2D
+ .TransformTwistAtSamePoint(
+ poseState.PoseInWorld,
+ twistInBody);
+
+ state = new VehicleState(
+ poseState.SampleTimestampSeconds,
+ poseState.PoseInWorld,
+ twistInWorld,
+ hasValidWheelSpeedEstimate);
+
+ LastFailureReason = string.Empty;
+ return true;
+ }
+ catch (Exception exception)
+ {
+ state = default;
+ LastFailureReason =
+ "舵轮电机反馈车体速度解算失败:" +
+ exception.Message;
+ return false;
+ }
+ }
+ }
+
+ ///
+ /// 读取最近一帧Detour纵向速度和轮速解算纵向速度,供实验记录使用。
+ ///
+ public bool TryGetLatestVelocityDiagnostics(
+ out double detourBodyVxMetersPerSecond,
+ out bool detourVelocityValid,
+ out double rawWheelBodyVxMetersPerSecond,
+ out double filteredWheelBodyVxMetersPerSecond,
+ out bool wheelVelocityValid)
+ {
+ lock (_syncRoot)
+ {
+ detourBodyVxMetersPerSecond =
+ _latestDetourBodyVxMetersPerSecond;
+ detourVelocityValid =
+ _latestDetourVelocityValid;
+ rawWheelBodyVxMetersPerSecond =
+ _latestRawWheelBodyVxMetersPerSecond;
+ filteredWheelBodyVxMetersPerSecond =
+ _latestFilteredWheelBodyVxMetersPerSecond;
+ wheelVelocityValid =
+ _latestWheelVelocityValid;
+ return _hasVelocityDiagnostics;
+ }
+ }
+
+ ///
+ /// 清除电机反馈速度的时间基准和低通滤波历史。
+ ///
+ public void Reset()
+ {
+ lock (_syncRoot)
+ {
+ _longitudinalSpeedFilter.Reset();
+ _wheelSpeedClock.Restart();
+ _hasPreviousTimestamp = false;
+ _previousTimestampSeconds = 0.0;
+ _hasVelocityDiagnostics = false;
+ _latestDetourBodyVxMetersPerSecond = 0.0;
+ _latestDetourVelocityValid = false;
+ _latestRawWheelBodyVxMetersPerSecond = 0.0;
+ _latestFilteredWheelBodyVxMetersPerSecond = 0.0;
+ _latestWheelVelocityValid = false;
+ LastFailureReason = string.Empty;
+ }
+ }
+
+ ///
+ /// 使用真实状态时间间隔更新纵向速度低通滤波,并在首帧建立基准。
+ ///
+ private double UpdateLongitudinalSpeedFilter(
+ double rawLongitudinalSpeedMetersPerSecond,
+ double timestampSeconds,
+ out bool hasValidWheelSpeedEstimate)
+ {
+ EnsureFiniteNonNegative(
+ timestampSeconds,
+ nameof(timestampSeconds));
+
+ if (!_hasPreviousTimestamp)
+ {
+ _longitudinalSpeedFilter.Reset(
+ rawLongitudinalSpeedMetersPerSecond);
+ _previousTimestampSeconds = timestampSeconds;
+ _hasPreviousTimestamp = true;
+ hasValidWheelSpeedEstimate = false;
+ return rawLongitudinalSpeedMetersPerSecond;
+ }
+
+ var deltaTimeSeconds =
+ timestampSeconds -
+ _previousTimestampSeconds;
+ _previousTimestampSeconds = timestampSeconds;
+
+ if (deltaTimeSeconds <= 0.0)
+ {
+ _longitudinalSpeedFilter.Reset(
+ rawLongitudinalSpeedMetersPerSecond);
+ hasValidWheelSpeedEstimate = false;
+ return rawLongitudinalSpeedMetersPerSecond;
+ }
+
+ hasValidWheelSpeedEstimate = true;
+ return _longitudinalSpeedFilter.Update(
+ rawLongitudinalSpeedMetersPerSecond,
+ deltaTimeSeconds);
+ }
+
+ ///
+ /// 检查采样时刻是否为非负有限值。
+ ///
+ private static void EnsureFiniteNonNegative(
+ double value,
+ string parameterName)
+ {
+ EnsureFinite(value, parameterName);
+
+ if (value < 0.0)
+ {
+ throw new ArgumentOutOfRangeException(
+ parameterName,
+ "采样时刻必须是非负有限值。");
+ }
+ }
+
+ ///
+ /// 检查状态输入是否为有限值。
+ ///
+ private static void EnsureFinite(
+ double value,
+ string parameterName)
+ {
+ if (double.IsNaN(value) ||
+ double.IsInfinity(value))
+ {
+ throw new ArgumentOutOfRangeException(
+ parameterName,
+ "车辆状态输入必须是有限值。");
+ }
+ }
+ }
+}
diff --git a/MultiWheelC/build/Clumsy/CommonUsage.dll b/MultiWheelC/build/Clumsy/CommonUsage.dll
index 82a8d1d..b4d199e 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 962de2a..6dfac63 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 9369a82..d70a5d3 100644
Binary files a/MultiWheelC/build/Clumsy/MultiWheelC.pdb and b/MultiWheelC/build/Clumsy/MultiWheelC.pdb differ
diff --git a/README.md b/README.md
index 0139fcb..8d9806d 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,8 @@
| 阶段 | 当前状态 | 说明 |
| --- | --- | --- |
| 1. 单车基本功能 | 联调中 | 已接入运动控制、MCU 通信、轮组反馈、急停 IO、电池、灯光、遥控和诊断代码,仍需持续实车验证 |
-| 2. 增加停车功能 | 部分开展 | 已提供夹臂控制、限位、报警和测试入口;轮胎识别、钻车和完整停车流程尚未实现 |
-| 3. 优化跟踪方法 | 已启动 | 已加入直线、圆弧、S 型、蟹行测试、实验 CSV 记录和 Python 绘图工具 |
+| 2. 增加停车功能 | 部分开展 | 已接入夹臂控制、限位和报警;夹臂动作测试当前已注释,轮胎识别、钻车和完整停车流程尚未实现 |
+| 3. 优化跟踪方法 | 已启动 | 保留旧版 `SendMotion` 测试,并新增 Stanley 横向 + PID 纵向控制、组合运动计划、实验 CSV 和新版绘图工具 |
| 4. 多车场景 | 暂不实施 | 多车参数和预研内容未参与当前编译,当前版本不提供多车联动 |
## 项目简介
@@ -41,7 +41,8 @@ MyParking 是一个面向多轮停车机器人底盘的 C# 工程,覆盖上层
| 单车运动 | 直线、圆弧、S 型轨迹,前进、蟹行和原地旋转 |
| 底盘命令 | `SendMotion`、`SendXYThSpeed` 和虚拟阿克曼测试后端 |
| 模式切换 | 正常、蟹行、自转模式;切换时先停车、预转舵轮并等待到位 |
-| 跟踪控制 | 终点跟踪、直线跟踪、基于 Detour 的直线跟踪和蟹行运动坐标系跟踪 |
+| 跟踪控制 | 旧版终点/直线/蟹行跟踪;新版 Stanley 横向控制、PID 纵向控制、前后 GCP 分配、轨迹偏离保护和终点状态判定 |
+| 状态估计 | Detour 位姿与差分速度;新版实验可保留 Detour 位姿并用舵轮反馈解算、低通滤波后的车体纵向速度替代其差分纵向速度 |
| 夹臂 | 左右夹臂速度命令、位置反馈、软限位、驱动报警、实体/虚拟遥控和目标位置动作 |
| MCU 通信 | 串口桥打开、复位、版本/状态查询、数字 IO、CAN/串口同步收发和异步回调 |
| 驱动与反馈 | 8 个驱动电机和 4 个舵轮的命令、速度/位置/舵角反馈及远程帧状态 |
@@ -99,7 +100,9 @@ MyParking/
│ └── commonusage/ # CommonUsage 公共底盘库源码
├── ref/ # 构建生成的 CommonUsage.dll(勿手工覆盖)
├── data_process/
-│ ├── 轨迹测试处理/ # 轨迹对比、误差、速度与角速度绘图
+│ ├── plot_new_controller_experiment.py # 新版控制器实验六子图工具
+│ ├── 新版控制器轨迹测试处理/ # 新版绘图工具的 Python 依赖
+│ ├── 旧版控制器轨迹测试处理/ # 旧版轨迹对比、误差和响应绘图
│ └── 电机响应处理/ # 舵轮响应快照分析
├── docs/
│ ├── SteeringConstraintDesign.md # 舵轮限位设计讨论
@@ -186,7 +189,7 @@ output/M/MedullaAdapter.dll
| CAN | 1 路,`500000 bit/s`,重试时间 `10 ms` |
| 串口 | 3 路,`9600 bit/s`,接收帧时间 `10 ms` |
| 电池通信端口索引 | `3` |
-| 自转最大角速度 | `30 deg/s` |
+| 遥控自转最大角速度 | `30 deg/s` |
| 轮速诊断目录 | `logs\wheel-speed` |
`docs/chassis参考.json` 是底盘参数样例;源码中尚未发现自动加载该文件的入口,实车参数仍应以宿主实际配置为准。
@@ -195,20 +198,20 @@ output/M/MedullaAdapter.dll
## 单车测试入口
-`MultiWheelC/MovementTests.cs` 当前注册:
+`MultiWheelC/Experiments` 当前启用以下宿主测试入口:
- `准备:四个舵轮与车头方向一致`
- `SendMotion:连续前进4m`
-- `SendXYThSpeed:原地自转90°`
-- `SendXYThSpeed:原地自转180°`
+- `SendXYThSpeed:输入角度原地自转`
- `SendMotion:左转90°半径2m圆弧`
- `SendMotion:蟹行直线4m`
- `SendMotion:蟹行左转90°半径2m圆弧`
- `SendMotion:4m S型曲线`
-- `夹臂关闭测试`
-- `夹臂启动测试`
+- `新版控制器:4m直线轨迹跟踪`
+- `新版控制器:直线-左半圆-直线轨迹跟踪`
+- `新版控制器:直线-圆弧-折线组合测试`
-这些测试由 Clumsy 宿主的测试界面执行,并不是 `dotnet test` 自动化测试。运动测试会按配置记录实验编号、参考轨迹、Detour 位姿和控制命令。
+这些测试由 Clumsy 宿主的测试界面执行,并不是 `dotnet test` 自动化测试。运动测试会按配置记录实验编号、参考轨迹、Detour 位姿、轮速解算速度和控制命令。`MultiWheelC/Experiments/ClampTests.cs` 中的夹臂测试目前整段注释,不会注册到宿主。
## 实验数据分析
@@ -224,14 +227,23 @@ Medulla 的轮速诊断可通过 `StartWheelSpeedDiagnostic` / `StopWheelSpeedDi
logs/wheel-speed/
```
-### 轨迹测试处理
+### 新版控制器轨迹处理
```powershell
-python -m pip install -r data_process\轨迹测试处理\requirements.txt
-python data_process\轨迹测试处理\run_all_plots.py "路径\实验1.csv" "路径\实验2.csv" --output-dir "路径\plots"
+python -m pip install -r data_process\新版控制器轨迹测试处理\requirements.txt
+python data_process\plot_new_controller_experiment.py "路径\实验1.csv" "路径\实验2.csv" --output-dir "路径\plots"
```
-默认重采样频率为 `20 Hz`,滤波窗口为 `0.55 s`,可通过 `--frequency` 和 `--window` 调整。
+该工具为每份新版控制器 CSV 生成一张六子图总图,包含轨迹、横向/航向误差、速度和前后 GCP/四舵轮转角。省略 CSV 参数时,它只扫描 `data_process` 根目录及其 `data` 子目录。
+
+### 旧版控制器轨迹处理
+
+```powershell
+python -m pip install -r data_process\旧版控制器轨迹测试处理\requirements.txt
+python data_process\旧版控制器轨迹测试处理\run_all_plots.py "路径\实验1.csv" "路径\实验2.csv" --output-dir "路径\plots"
+```
+
+旧版工具默认重采样频率为 `20 Hz`,滤波窗口为 `0.55 s`,可通过 `--frequency` 和 `--window` 调整。
### 电机响应处理
diff --git a/README_en.md b/README_en.md
index c52af29..af35086 100644
--- a/README_en.md
+++ b/README_en.md
@@ -16,8 +16,8 @@ The current work remains focused on the **single robot** and is in chassis integ
| Stage | Current status | Notes |
| --- | --- | --- |
| 1. Basic single-robot functions | Integration in progress | Motion control, MCU communication, wheel feedback, emergency-stop I/O, battery, lights, remote control, and diagnostics are connected in code; physical validation is ongoing |
-| 2. Add parking functions | Partially started | Clamp control, limits, alarms, and test entries exist; tire recognition, vehicle entry, and the complete parking workflow are not implemented |
-| 3. Improve tracking | Started | Straight, arc, S-curve, and crab tests, experiment CSV recording, and Python plotting tools are available |
+| 2. Add parking functions | Partially started | Clamp control, limits, and alarms are connected; the clamp movement tests are currently commented out, while tire recognition, vehicle entry, and the complete parking workflow are not implemented |
+| 3. Improve tracking | Started | Legacy `SendMotion` tests remain, with new Stanley lateral + PID longitudinal control, composite motion plans, experiment CSVs, and a new plotting tool |
| 4. Multi-robot scenarios | Deferred | Multi-robot R&D settings are excluded from the build, and the current version provides no fleet coordination |
## Overview
@@ -41,7 +41,8 @@ No ROS/ROS 2, Docker, or Web simulator project is present. The plugins are loade
| Single-robot motion | Straight, arc, and S-curve paths; forward, crab, and in-place rotation |
| Chassis commands | `SendMotion`, `SendXYThSpeed`, and a virtual-Ackermann test backend |
| Mode switching | Normal, crab, and spin modes; stop, pre-steer, and wait for wheel alignment before motion |
-| Tracking | Destination tracking, line tracking, Detour-based line tracking, and crab motion-frame tracking |
+| Tracking | Legacy destination, line, and crab tracking; new Stanley lateral control, PID longitudinal control, front/rear GCP allocation, path-deviation protection, and terminal-state checks |
+| State estimation | Detour pose and differentiated velocity; new experiments can retain the Detour pose while replacing its differentiated longitudinal velocity with a low-pass-filtered body velocity derived from steer-wheel feedback |
| Clamp | Left/right speed commands, position feedback, soft limits, driver alarms, physical/virtual remote control, and target-position actions |
| MCU communication | Bridge open/reset, version/state queries, digital I/O, synchronous serial/CAN access, and asynchronous callbacks |
| Drive and feedback | Commands and speed/position/steering feedback for eight drive motors and four steer modules, plus remote-frame state |
@@ -99,7 +100,9 @@ MyParking/
│ └── commonusage/ # CommonUsage chassis-library source
├── ref/ # Generated CommonUsage.dll (do not overwrite by hand)
├── data_process/
-│ ├── 轨迹测试处理/ # Trajectory comparison, error, speed, and yaw plots
+│ ├── plot_new_controller_experiment.py # Six-panel plots for new-controller trials
+│ ├── 新版控制器轨迹测试处理/ # Python dependencies for the new plotting tool
+│ ├── 旧版控制器轨迹测试处理/ # Legacy trajectory, error, and response plots
│ └── 电机响应处理/ # Steering-response snapshot analysis
├── docs/
│ ├── SteeringConstraintDesign.md # Steering-limit design notes
@@ -186,7 +189,7 @@ MCU defaults confirmed from the current source:
| CAN | One channel at `500000 bit/s`, with a `10 ms` retry time |
| Serial | Three channels at `9600 bit/s`, with a `10 ms` receive-frame time |
| Battery port index | `3` |
-| Maximum spin rate | `30 deg/s` |
+| Maximum remote-control spin rate | `30 deg/s` |
| Wheel-speed diagnostic directory | `logs\wheel-speed` |
`docs/chassis参考.json` is a chassis-parameter example. No automatic loader for it was found in the source. Treat the actual host configuration as authoritative.
@@ -195,20 +198,20 @@ Before physical testing, verify the port, vehicle ID, steering zero and limits,
## Single-Robot Test Entries
-`MultiWheelC/MovementTests.cs` currently registers:
+`MultiWheelC/Experiments` currently enables these host test entries:
- `准备:四个舵轮与车头方向一致`
- `SendMotion:连续前进4m`
-- `SendXYThSpeed:原地自转90°`
-- `SendXYThSpeed:原地自转180°`
+- `SendXYThSpeed:输入角度原地自转`
- `SendMotion:左转90°半径2m圆弧`
- `SendMotion:蟹行直线4m`
- `SendMotion:蟹行左转90°半径2m圆弧`
- `SendMotion:4m S型曲线`
-- `夹臂关闭测试`
-- `夹臂启动测试`
+- `新版控制器:4m直线轨迹跟踪`
+- `新版控制器:直线-左半圆-直线轨迹跟踪`
+- `新版控制器:直线-圆弧-折线组合测试`
-These are run through the Clumsy host's test interface and are not an automated `dotnet test` suite. Motion tests record the experiment number, reference path, Detour pose, and control commands according to their configuration.
+These are run through the Clumsy host's test interface and are not an automated `dotnet test` suite. Motion tests record the experiment number, reference path, Detour pose, wheel-derived velocity, and control commands according to their configuration. The clamp tests in `MultiWheelC/Experiments/ClampTests.cs` are currently commented out in full and are not registered with the host.
## Experiment Data Analysis
@@ -224,14 +227,23 @@ Medulla wheel-speed diagnostics can be controlled with the `StartWheelSpeedDiagn
logs/wheel-speed/
```
-### Trajectory processing
+### New-controller trajectory processing
```powershell
-python -m pip install -r data_process\轨迹测试处理\requirements.txt
-python data_process\轨迹测试处理\run_all_plots.py "path\trial1.csv" "path\trial2.csv" --output-dir "path\plots"
+python -m pip install -r data_process\新版控制器轨迹测试处理\requirements.txt
+python data_process\plot_new_controller_experiment.py "path\trial1.csv" "path\trial2.csv" --output-dir "path\plots"
```
-The default resampling frequency is `20 Hz`, and the default filter window is `0.55 s`; use `--frequency` and `--window` to change them.
+This tool creates one six-panel summary for each new-controller CSV, covering the path, lateral/heading errors, speed, and front/rear GCP and four-wheel steering angles. When no CSV is passed, it scans only the `data_process` root and its `data` subdirectory.
+
+### Legacy-controller trajectory processing
+
+```powershell
+python -m pip install -r data_process\旧版控制器轨迹测试处理\requirements.txt
+python data_process\旧版控制器轨迹测试处理\run_all_plots.py "path\trial1.csv" "path\trial2.csv" --output-dir "path\plots"
+```
+
+The legacy tool defaults to `20 Hz` resampling and a `0.55 s` filter window; use `--frequency` and `--window` to change them.
### Steering-response processing
diff --git a/data_process/新版控制器轨迹测试处理/plot_new_controller_experiment.py b/data_process/plot_new_controller_experiment.py
similarity index 98%
rename from data_process/新版控制器轨迹测试处理/plot_new_controller_experiment.py
rename to data_process/plot_new_controller_experiment.py
index c7a7fcb..8af14eb 100644
--- a/data_process/新版控制器轨迹测试处理/plot_new_controller_experiment.py
+++ b/data_process/plot_new_controller_experiment.py
@@ -530,10 +530,16 @@ def plot_experiment(
label="Detour估计Vx",
)
if np.any(np.isfinite(data["wheel_filtered_speed"])):
- axis.plot(
- data["time"],
- data["wheel_filtered_speed"],
- linewidth=1.5,
+ wheel_filtered_valid = np.isfinite(
+ data["wheel_filtered_speed"]
+ )
+ axis.scatter(
+ data["time"][wheel_filtered_valid],
+ data["wheel_filtered_speed"][wheel_filtered_valid],
+ color="tab:red",
+ s=14,
+ marker="o",
+ zorder=5,
label="轮速解算滤波Vx(控制使用)",
)
else:
diff --git a/data_process/新版前后角解耦控制器测试处理/4m0.4/记录.txt b/data_process/新版前后角解耦控制器测试处理/4m0.4/记录.txt
new file mode 100644
index 0000000..e69de29
diff --git a/data_process/新版前后角解耦控制器测试处理/4m0.4偏10/记录.txt b/data_process/新版前后角解耦控制器测试处理/4m0.4偏10/记录.txt
new file mode 100644
index 0000000..c4080c8
--- /dev/null
+++ b/data_process/新版前后角解耦控制器测试处理/4m0.4偏10/记录.txt
@@ -0,0 +1,21 @@
+第一次:
+: * (Exception):DriveTask failed, msg=车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.040m,航向误差=0.15°。, stack:
+ at ClumsyCore.DriveTask.Wait() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 205
+ at MultiWheelC.NewControllerStraight4mTest.Test() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Experiments\NewControllerTrackingTests.cs:line 246
+ at ClumsyLite.ClumsyLiteUI.<>c__DisplayClass19_1.b__21() in D:\MDCS\Source\Core\Clumsy\ClumsyLite\ClumsyLiteUI.cs:line 592
+
+ *p.InnerException * (InvalidOperationException):车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.040m,航向误差=0.15°。, stack:
+ at MultiWheelC.TrajectoryTrackingMovement.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\TrajectoryTrackingMovement.cs:line 257
+ at ClumsyCore.DriveTask.<>c__DisplayClass9_0.<.ctor>b__2() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 112
+
+第三次:
+
+: * (Exception):DriveTask failed, msg=车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.032m,航向误差=0.10°。, stack:
+ at ClumsyCore.DriveTask.Wait() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 205
+ at MultiWheelC.NewControllerStraight4mTest.Test() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Experiments\NewControllerTrackingTests.cs:line 246
+ at ClumsyLite.ClumsyLiteUI.<>c__DisplayClass19_1.b__21() in D:\MDCS\Source\Core\Clumsy\ClumsyLite\ClumsyLiteUI.cs:line 592
+
+ *p.InnerException * (InvalidOperationException):车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.032m,航向误差=0.10°。, stack:
+ at MultiWheelC.TrajectoryTrackingMovement.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\TrajectoryTrackingMovement.cs:line 257
+ at ClumsyCore.DriveTask.<>c__DisplayClass9_0.<.ctor>b__2() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 112
+
diff --git a/data_process/新版前后角解耦控制器测试处理/直线圆弧折线/记录.txt b/data_process/新版前后角解耦控制器测试处理/直线圆弧折线/记录.txt
new file mode 100644
index 0000000..0b5dc62
--- /dev/null
+++ b/data_process/新版前后角解耦控制器测试处理/直线圆弧折线/记录.txt
@@ -0,0 +1,22 @@
+第二次:
+: * (Exception):DriveTask failed, msg=车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.037m,航向误差=0.27°。, stack:
+ at ClumsyCore.DriveTask.Wait() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 205
+ at MultiWheelC.CompositeStopTurnGoTest.Test() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Experiments\CompositeMotionPlanTests.cs:line 211
+ at ClumsyLite.ClumsyLiteUI.<>c__DisplayClass19_1.b__21() in D:\MDCS\Source\Core\Clumsy\ClumsyLite\ClumsyLiteUI.cs:line 592
+
+ *p.InnerException * (InvalidOperationException):车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.037m,航向误差=0.27°。, stack:
+ at MultiWheelC.TrajectoryTrackingMovement.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\TrajectoryTrackingMovement.cs:line 257
+ at MultiWheelC.MotionPlanExecutor.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\MotionPlanExecutor.cs:line 174
+ at ClumsyCore.DriveTask.<>c__DisplayClass9_0.<.ctor>b__2() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 112
+
+第二次:
+: * (Exception):DriveTask failed, msg=车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.033m,航向误差=0.27°。, stack:
+ at ClumsyCore.DriveTask.Wait() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 205
+ at MultiWheelC.CompositeStopTurnGoTest.Test() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Experiments\CompositeMotionPlanTests.cs:line 211
+ at ClumsyLite.ClumsyLiteUI.<>c__DisplayClass19_1.b__21() in D:\MDCS\Source\Core\Clumsy\ClumsyLite\ClumsyLiteUI.cs:line 592
+
+ *p.InnerException * (InvalidOperationException):车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.033m,航向误差=0.27°。, stack:
+ at MultiWheelC.TrajectoryTrackingMovement.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\TrajectoryTrackingMovement.cs:line 257
+ at MultiWheelC.MotionPlanExecutor.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\MotionPlanExecutor.cs:line 174
+ at ClumsyCore.DriveTask.<>c__DisplayClass9_0.<.ctor>b__2() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 112
+
diff --git a/data_process/新版前后角解耦控制器测试处理/直线圆弧直线/记录.txt b/data_process/新版前后角解耦控制器测试处理/直线圆弧直线/记录.txt
new file mode 100644
index 0000000..3a845c1
--- /dev/null
+++ b/data_process/新版前后角解耦控制器测试处理/直线圆弧直线/记录.txt
@@ -0,0 +1,10 @@
+第二次:
+: * (Exception):DriveTask failed, msg=车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.034m,航向误差=0.11°。, stack:
+ at ClumsyCore.DriveTask.Wait() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 205
+ at MultiWheelC.NewControllerStraightSemicircleStraightTest.Test() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Experiments\NewControllerTrackingTests.cs:line 613
+ at ClumsyLite.ClumsyLiteUI.<>c__DisplayClass19_1.b__21() in D:\MDCS\Source\Core\Clumsy\ClumsyLite\ClumsyLiteUI.cs:line 592
+
+ *p.InnerException * (InvalidOperationException):车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.034m,航向误差=0.11°。, stack:
+ at MultiWheelC.TrajectoryTrackingMovement.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\TrajectoryTrackingMovement.cs:line 257
+ at ClumsyCore.DriveTask.<>c__DisplayClass9_0.<.ctor>b__2() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 112
+
diff --git a/output/C/CommonUsage.dll b/output/C/CommonUsage.dll
index 82a8d1d..b4d199e 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 962de2a..6dfac63 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 9369a82..d70a5d3 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 82a8d1d..b4d199e 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 b225c77..0fca26f 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 f755db3..246e2b6 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 82a8d1d..b4d199e 100644
Binary files a/ref/CommonUsage.dll and b/ref/CommonUsage.dll differ