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

This commit is contained in:
2026-08-19 17:38:17 +08:00
parent d8de901a80
commit 0d5539e595
55 changed files with 4473 additions and 387 deletions
+15
View File
@@ -0,0 +1,15 @@
# MyParking 知识库导航
默认只读本页,再按任务选择1~2份文档;不要一次加载全部知识库。
| 文档 | 内容 | 适合任务 |
| --- | --- | --- |
| `overview.md` | 项目背景、车辆和运行目标 | 初次了解项目、业务范围判断 |
| `architecture.md` | 模块职责、入口、调用链和数据流 | 定位代码、评估结构调整 |
| `interfaces.md` | 关键接口、数据模型、坐标、单位和通信边界 | 修改控制、轨迹、状态、底盘或MCU接口 |
| `decisions.md` | 已实施方案、未实施决定和待评估方向 | 方案选择、避免推翻既有约束 |
| `problems.md` | 已解决、待解决和待验证问题 | 排障、实验设计、回归检查 |
| `progress.md` | 当前能力、进行中工作、阻塞和下一步 | 恢复近期开发上下文 |
| `detour-information-checklist.md` | 需要向 Detour 负责人确认的最小信息清单 | 对接定位接口、确认时间戳和定位质量语义 |
读取建议:先用类名或路径定位代码;只有涉及长期背景时才读对应文档。涉及旧版或MDCS时,再按任务读取工作区参考目录,并明确来源。
-225
View File
@@ -1,225 +0,0 @@
// 然后因为我这个车的舵轮是有正负120°限制的,所以有这种情况,比如我给了+90°让它进入蟹行模式,然后我在蟹行模式我去追一个相对于车左侧方向+40°的轨迹,那这时候就超过限制了,那我这是要做一个什么措施吗,比如我能想到的就是达到这个限位要么就停车打印说明到达限位,安全措施;要么就是我在追这个轨迹的过程中知道我大概可能超过限位,然后我提前停下,自转,调整为车头朝向为轨迹的方向,也就是自转调整到我车当前模式的初始姿态方向这种;你有什么好的建议吗,其他的建议吗,或者有什么可行的方法,因为实际的过程还是需要考虑这个限位的问题的
// 9:49
// 这个问题必须考虑,但“目标轮角超过 +120°”不一定意味着命令无法执行。对于舵轮,首先要利用“轮角反向、轮速反向”的等价关系,然后才考虑停车和车身重定向。
// 1. +130° 不一定超过限位后就无法运动
// 你的例子是:
// 蟹行基准方向:+90°
// 轨迹又向该方向增加:+40°
// 期望运动方向:+130°
// 舵轮限制:[-120°, +120°]
// 直接使用:
// 轮角:+130°
// 轮速:+0.3 m/s
// 确实超过了 +120°。
// 但它有一个完全等价的表达:
// 轮角:-50°
// 轮速:-0.3 m/s
// 因为:
// (θ, v) ≡ (θ - 180°, -v)
// 即:
// (+130°, +0.3) ≡ (-50°, -0.3)
// 虽然轮子的机械朝向和转速符号变了,但轮胎与地面的速度向量完全相同。
// 所以第一层措施应当是:
// 先尝试期望角度
// 如果超过限位
// 尝试角度±180°
// 同时将轮速取反
// 如果等价角度满足限位
// 使用等价解
// 否则才判定无法执行
// 你当前的 CommonUsage.dll 中已经有类似的舵角反解逻辑,SendXYThSpeed() 返回 bool 也是为了告诉上层运动分解是否成功。
// 2. 你的±120°结构在静态上覆盖了所有运动方向
// 因为:
// 可转范围总宽度 = 240°
// 大于 180°。
// 只要驱动轮允许正反转,那么对于任意二维速度方向,通常都能在下面两种表达中找到一个合法值:
// (θ, +v)
// (θ ± 180°, -v)
// 因此单纯从“某一时刻目标速度方向”看,+130° 可以用 -50° 表达,不一定需要旋转车身。
// 真正困难的是动态过程:
// 当前轮角:+90°
// 新目标等价轮角:-50°
// 需要转动:-140°
// 虽然最终角度合法,但舵轮需要转过很大的角度。在舵轮没有到位前,不能继续以正常速度驱动车辆,否则实际运动方向会与轨迹命令不一致。
// 所以核心问题不是“有没有合法终态”,而是:
// 从当前轮角切换到合法目标轮角的过程是否安全、连续。
// 3. 建议采用四层处理策略
// 第一层:等价舵角选择
// 为每一个舵轮计算速度向量:
// Vix = Vx - ω·yi
// Viy = Vy + ω·xi
// 然后:
// θi = atan2(Viy, Vix)
// si = sqrt(Vix² + Viy²)
// 尝试候选解:
// 候选1(θi, si)
// 候选2(θi + 180°, -si)
// 候选3(θi - 180°, -si)
// 过滤掉超过 [-120°, +120°] 的候选,再选择相对当前机械轮角转动最小的候选。
// 这里计算机械转角距离时,不要简单把差值归一化成最短圆周角,因为舵轮不能无限旋转。对于有限机械区间,应当直接比较:
// Math.Abs(candidateAngle - currentMechanicalAngle)
// 第二层:软限位
// 不要把 ±120° 当作正常工作的边界,建议设置软限位,例如:
// 物理硬限位:[-120°, +120°]
// 软件工作限位:[-105°, +105°]
// 预警区域:[105°, 120°]
// 具体余量需要实车标定,初期可以保留 10°~20°。
// 状态可以分成:
// |θ| < 100° 正常
// 100°~105° 接近限位,开始规划
// 105°~120° 限速或停止切换
// >120° 禁止下发并报警
// 软限位的意义是给控制器留出制动、舵轮响应和误差空间。
// 第三层:舵轮转向期间限制驱动速度
// 如果新的合法舵角与当前舵角相差很大,例如:
// 当前:+90°
// 目标:-50°
// 差值:140°
// 不应当马上正常行驶,应进入:
// 减速
// ↓
// 驱动速度降为0
// ↓
// 舵轮转到-50°
// ↓
// 确认所有舵轮到位
// ↓
// 轮速使用负值重新起步
// 可以设置阈值:
// 舵角误差 < 2°:允许正常起步
// 舵角误差 2°~10°:低速或保持
// 舵角误差 > 10°:驱动速度为0
// 实际阈值根据舵向响应速度标定。
// 需要注意:把所有 Vx、Vy、Omega 同比例减小,只能降低运动速度,通常不会改变目标舵轮角度。因此“减速”能让切换更安全,但不一定能让超限角度重新变得合法。
// 4. 车身重定向是更平滑的高级方案
// 你提出的:
// 提前停车,然后自转,调整车头朝向,再继续跟踪。
// 这是正确而且很实用的方案,尤其适合连续轨迹跟踪。
// 但不一定要把车头完全转到轨迹方向,应当根据当前驾驶模式选择车身朝向。
// 假设轨迹方向相对当前车体为:
// +130°
// 如果希望继续保持蟹行模式,舵轮的优选方向是 +90°,那么只需要让车体逆时针自转:
// 130° - 90° = 40°
// 车体旋转 +40° 后:
// 世界轨迹方向不变
// 轨迹在新车体坐标系中的方向:+90°
// 舵轮回到标准蟹行角度:+90°
// 不需要让车头旋转完整的 130°。
// 如果准备切换成正常前进模式,优选舵角是 0°,才需要让车头基本对准轨迹:
// 130° - 0° = 130°
// 因此可以使用下面的通用公式:
// 期望车身朝向 =
// 世界轨迹切线方向 - 当前模式的优选舵轮方向
// 其中:
// 正常模式优选角:0°
// 左蟹行优选角:+90°
// 右蟹行优选角:-90°
// 对于蟹行轨迹,车身朝向参考值为:
// yawReference =
// pathTangentWorldRadians -
// Math.PI / 2.0;
// 这样车体会逐渐调整自身朝向,使轨迹方向始终落在舵轮舒适区,而不是一直顶着 ±120° 工作。
// 5. 不建议到达硬限位才突然自转
// 更好的方案是向前预测。
// 轨迹跟踪本身通常有前视点,可以提前计算未来一段轨迹对应的目标舵角:
// 当前时刻目标轮角
// 未来0.2秒目标轮角
// 未来0.5秒目标轮角
// 未来1.0秒目标轮角
// 一旦预测到未来会进入软限位,就提前触发车身重定向:
// 正常跟踪
// ↓
// 预测舵角接近软限位
// ↓
// 逐渐降低线速度
// ↓
// 停车
// ↓
// 车身原地旋转
// ↓
// 舵轮调整到模式初始方向
// ↓
// 重新捕获轨迹
// ↓
// 继续跟踪
// 可以定义状态机:
// internal enum TrackingConstraintState
// {
// Tracking,
// ApproachingSteeringLimit,
// Stopping,
// ReorientingBody,
// AligningWheels,
// Resuming,
// Fault
// }
// 不要在一个控制周期内同时完成停车、车身旋转和恢复跟踪。
// 6. LQR和限位处理应该分层
// 以后写 LQR 时,不建议让基础 LQR 直接承担全部舵轮限位逻辑。
// 推荐结构:
// 轨迹参考
// ↓
// LQR轨迹控制器
// 输出期望 Vx、Vy、Omega
// ↓
// 舵轮约束管理器
// 等价角选择、软限位、预测、车身重定向
// ↓
// ChassisCommand
// ↓
// MultiWheelChassisAdapter
// ↓
// SendXYThSpeed
// 可以以后增加一个文件:
// Shared/SteeringConstraintManager.cs
// 它负责:
// 根据 Vx、Vy、Omega 预测每个舵轮目标角。
// 选择“舵角反向、轮速反向”的等价解。
// 检查硬限位和软限位。
// 判断是否需要停车对齐。
// 给出是否需要重新规划车身朝向。
// 记录哪个舵轮导致不可执行。
// 而 FrameTransform2D、ChassisCommand 不应该包含这些车辆机械约束。
// 7. 当前阶段建议先做到什么
// 现在先实现最小安全版本:
// 使用 SendXYThSpeed(),让底层优先选择等价舵角。
// 检查 adapter.Send(command) 的返回值。
// 返回 false 时立即停车,不能继续沿用上一条速度。
// 打印每个舵轮的当前角度、目标角度和上下限。
// 模式切换时先停车、预转舵轮、确认到位,再开放摇杆。
// 先用较低速度测试 +90° → +110° → +120° → +130° 的过程。
// 最低限度的发送逻辑应当是:
// var success = adapter.Send(command, interval);
// if (!success)
// {
// adapter.Stop(interval);
// Console.WriteLine(
// "底盘命令分解失败,已停车:" +
// "可能存在舵角限制或舵轮无法完成方向切换。");
// }
// 然后再实现预测重定向。
// 推荐的最终策略
// 对于你的停车机器人,我建议采用:
// 优先级1:使用θ±180°、轮速反向的等价解
// 优先级2:选择机械转角变化较小的等价解
// 优先级3:接近软限位时降低速度
// 优先级4:提前规划车身朝向,保持舵轮在舒适角度
// 优先级5:无法连续切换时停车、自转、重新对齐
// 优先级6:仍不可行时保持停车并报警
// 所以你的 +130° 示例不应该一开始就判故障。它可以先使用 -50° + 反向轮速。如果从当前 +90° 切换到 -50° 的动态过程不安全,再停车调整。若轨迹允许改变车身姿态,更理想的办法是让车身提前旋转 +40°,从而继续以标准 +90° 蟹行姿态跟踪,而不是一直逼近机械硬限位。
+129
View File
@@ -0,0 +1,129 @@
# 系统架构
## 解决方案与依赖边界
`ParkingRobot.sln` 包含三个项目:
| 项目 | 目标框架 | 职责 |
| --- | --- | --- |
| `CommonUsage-MultiVehicleSync/commonusage/CommonUsage.csproj` | `netstandard2.0` | 通用底盘、轮子模型、`SendMotion`/`SendXYThSpeed`和四轮几何解算 |
| `MedullaAdapter/MedullaAdapter.csproj` | `net8.0` | Medulla M层插件、MCU/CAN/串口/IO、遥控、报警和硬件反馈 |
| `MultiWheelC/MultiWheelC.csproj` | `netstandard2.0` | Clumsy C层插件、动作、控制器、轨迹、状态估计、实验记录 |
`Shared/` 没有独立项目:`MultiWheelC` 链接全部 `Shared/**/*.cs``MedullaAdapter` 只链接其需要的模型、数学、校验和底盘适配文件。`CommonUsage` 是独立底盘库,不反向依赖 `Shared`、M层或C层。来源:三个 `.csproj`
```text
Clumsy宿主
└─ MultiWheelC
├─ Trajectory / StateEstimation / Control / Movements
├─ Shared
└─ CommonUsage.dll
Medulla宿主
└─ MedullaAdapter
├─ Shared(链接的必要文件)
├─ CommonUsage.dll
└─ mcu_serial_bridge.dll → MCU → CAN / Serial / IO
```
## 目录职责
| 路径 | 当前职责 |
| --- | --- |
| `MultiWheelC/Configuration/` | 停车控制、状态估计、原地自转和完成条件的车辆级运行参数 |
| `MultiWheelC/Trajectory/` | 弧长参数化轨迹、插值、投影和进度窗口;`LegacyTrackAdapter.cs` 目前仅占位 |
| `MultiWheelC/StateEstimation/` | Detour位姿校验/差分速度、轮组反馈速度组合与低通滤波 |
| `MultiWheelC/Control/Abstractions/` | 横向、纵向控制器接口与周期输入/输出模型 |
| `MultiWheelC/Control/Lateral/` | 当前默认 `StanleyLateralController` |
| `MultiWheelC/Control/Longitudinal/` | 当前默认 `PidLongitudinalController` |
| `MultiWheelC/Control/Allocation/` | 横纵结果组合、GCP限幅及GCP与刚体速度的转换 |
| `MultiWheelC/Control/Execution/` | 单周期编排、终点策略、命令执行和耗时诊断 |
| `MultiWheelC/Movements/` | 舵轮准备、轨迹跟踪、原地自转和组合动作计划 |
| `MultiWheelC/Experiments/` | Clumsy宿主人工测试、测试轨迹工厂和CSV记录 |
| `MultiWheelC/Old/` | 保留的旧实现;不能仅因仍参与编译就视为新版流程依赖 |
| `Shared/` | M/C共享的SI数据模型、坐标变换、数值校验和底盘适配 |
| `MedullaAdapter/` | 车型定义、LadderLogic、MCU桥、CAN/串口、遥控、诊断 |
| `CommonUsage-MultiVehicleSync/commonusage/Chassis/` | 实际轮子模型、GCP/ICR求解、舵角/轮速分配和机械约束 |
| `data_process/` | 离线实验数据处理;日常代码任务不扫描其中的实验日志 |
| `参考文档/` | 参数样例和历史设计资料;不等同于运行时配置 |
## 入口与宿主生命周期
### C层
- `PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefinition>` 是Clumsy车型定义和M/C IO边界。
- `PilotDefinition.Conf` 是动作读取运行配置的统一入口;`PilotConfig` 字段提供元数据和默认值。
- `[MovementTest]` 类型由宿主发现并执行,例如 `NewControllerStraight4mTest``NewControllerReverseStraight4mTest``NewControllerCrab45Straight4mTest``TestRotateAngle``CompositeStopTurnGoTest`
- `MovementDefinition.Get()``IEnumerable<bool>` 形式协作执行:`true` 表示继续,结束动作时返回/产生 `false` 或退出枚举。具体宿主调度细节来自外部程序集,仓库内不可完全确认。
### M层
- `DiverCartDefinition : MultiWheelCartDefinition` 是Medulla车型定义。
- `CommunicationInit()` 打开并配置MCU桥。
- `[UseLadderLogic]` 注册 `AlarmRoutine`50ms)、`MotorRoutine`50ms)和 `MCURoutine`(20ms);这些是声明的扫描间隔,不能直接等同于实测稳定周期。
- `[UseManualController]` 注册 `Remote`
## 新版轨迹跟踪调用链
```text
MovementTest / MotionPlanExecutor
→ TrajectoryTrackingMovement.Get()
→ PrepareWheelsForward(DirectionRadians=β)
→ MultiWheelChassisAdapter.ActivateMotionFrame(β)
→ ParkingVehicleStateProviderFactory.Create()
→ ParkingGeometricController.Start()/ExecuteCycle()
→ IVehicleStateProvider.TryGetState()
→ TrajectoryProjector.Project()
→ ILateralController.Compute()
→ ILongitudinalController.ComputeSpeedMetersPerSecond()
→ GcpCommandAllocator.Allocate()
→ GcpCommandExecutor.Execute()
→ GcpKinematics.ToBodyTwist()
→ MultiWheelChassisAdapter.SendBodyTwist()
→ MultiWheelChassis.SendMotion()
→ 四个真实舵轮角度和速度
```
`TrajectoryTrackingMovement` 默认从 `PilotDefinition.Conf` 读取车辆级参数,同时保留少量动作级覆盖字段;横向控制器可通过 `LateralControllerFactory` 替换,纵向控制器当前固定创建为 `PidLongitudinalController`
## 状态数据流
```text
DetourInterface.getCartLocation()
→ DetourVehicleStateProvider
├─ 位姿单位转换、重复帧处理、跳变/预测残差校验
└─ VelocityEstimator2DDetour差分Vx/Vy/Omega
MultiWheelChassis.GetCarSpeed(true)
→ WheelFeedbackVehicleStateProvider
├─ Vx、Vy分别使用同一时间常数低通滤波
├─ 覆盖Detour线速度
└─ 保留Detour位姿和Omega
→ VehicleState(世界位姿、世界Twist、车体Twist)
→ ParkingGeometricController
```
## 运动坐标系与四轮解算数据流
`MultiWheelChassisAdapter` 对外只接收真实车体系 `Twist2D`。滚动动作开始前先固定运动方向β:
1. `ActivateMotionFrame(β)` 调用 `MultiWheelChassis.SetOriginBias(0,0,-β)`
2. 车体线速度通过 `R(-β)` 表达到运动坐标系。
3. 运动系中以 `±ControlPointRadius` 作为虚拟前后GCP,计算GCP方向。
4. `MultiWheelChassis.SendMotion` 由两个GCP方向求瞬时旋转中心ICR。
5. 每个真实轮子的 `PhysicalPosition` 被旋转成运动系 `Position`,再由ICR分别求切线角和速度半径比例。
6. `sTh - sw.ZeroDirection` 把运动系方向转成真实舵轮机械命令;距离和速度大小不因坐标旋转改变。
关键位置:`Shared/Chassis/MultiWheelChassisAdapter.cs``MultiWheelChassis.cs::SetOriginBias``SendMotion``CalculateAxes`
## 动作组织
- `PrepareWheelsForward`:停车、下发任意固定方向β并等待稳定;名称保留“Forward”,但功能已支持非零方向。
- `TrajectoryTrackingMovement`:准备运动系、创建控制器、周期执行、失败停车,可选完成后回正。
- `MultiWheelRotateInPlace`:准备自转舵角、交接到XYTh解算、世界航向PID、完成后回正。
- `MotionPlanExecutor`:开始运动前预检全部段,顺序执行 `TrackMotionPlanSegment``RotateInPlaceMotionPlanSegment`,共享状态源。
## 构建与打包
`build-and-package.ps1` 顺序构建 `CommonUsage`、M层和C层,将新 `CommonUsage.dll` 复制到 `ref/`,最后生成 `output/M``output/C`。两个部署包必须使用同一份 `CommonUsage.dll`。构建产物目录不作为知识来源,也不直接编辑。
-95
View File
@@ -1,95 +0,0 @@
{
"MaxManualSpeed": 0.3,
"MaxManualAngularSpeed": 45.0,
"test3": 24,
"trigger": 0,
"ManualArmSpeedFac": 5.0,
"CarNum": 1,
"MusicVolume": 5,
"LeftArmLowerPos": -76358,
"LeftArmUpperPos": 38224,
"RightArmLowerPos": -41976,
"RightArmUpperPos": 73519,
"MCUPort": "COM3",
"TransmitterSpeedUpperLimit": 1.0,
"TransmitterSpeedLowerLimit": 0.0,
"ThBiasLeftFront": -342.0,
"ThBiasLeftRear": -185.35,
"ThBiasRightFront": -58.5,
"ThBiasRightRear": -1198.85,
"TurnAccSpeed": 1000.0,
"MaxManualTheta": 45.0,
"IsDiffSteer": true,
"ManualThetaPow": 2.0,
"DiffSteerKp": 0.003,
"DiffSteerKi": 0.0,
"DiffSteerKd": 0.0,
"DiffSteerMaxI": 0.0,
"DiffSteerThresh": 0.3,
"DiffSteerDeadZone": 0.1,
"DiffSteerSpeedAcc": 1.0,
"Ghost_ActualSpeedLeftFront_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedLeftFront_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedLeftRear_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedLeftRear_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedRightFront_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedRightFront_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedRightRear_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedRightRear_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedLeftFrontLeft_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedLeftFrontLeft_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedLeftFrontRight_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedLeftFrontRight_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedLeftRearLeft_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedLeftRearLeft_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedLeftRearRight_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedLeftRearRight_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedRightFrontLeft_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedRightFrontLeft_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedRightFrontRight_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedRightFrontRight_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedRightRearLeft_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedRightRearLeft_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualSpeedRightRearRight_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualSpeedRightRearRight_GhostPIDUpdater_threshold": 200.0,
"Ghost_ActualThLeftFront_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualThLeftFront_GhostPIDUpdater_threshold": 5.0,
"Ghost_ActualThLeftRear_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualThLeftRear_GhostPIDUpdater_threshold": 5.0,
"Ghost_ActualThRightFront_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualThRightFront_GhostPIDUpdater_threshold": 5.0,
"Ghost_ActualThRightRear_GhostPIDUpdater_kp": 0.5,
"Ghost_ActualThRightRear_GhostPIDUpdater_threshold": 5.0,
"WheelAngleChangeLimit": 20.0,
"ElectricDebugMode": false,
"PostGPSToDetour": false,
"GPSBaseLongitude": 0.0,
"GPSBaseLatitude": 0.0,
"GPSInCarX": 0.0,
"GPSInCarY": 0.0,
"GPSInCarTh": 0.0,
"LowBatteryAlarmThreshold": 15.0,
"LowBatteryStopThreshold": 10.0,
"GhostMode": false,
"QrMode": false,
"GhostEnableSimBattery": false,
"GhostTimeBatteryFac": 600.0,
"ReductionRatio": 1.74,
"WheelDiameter": 165.0,
"CloseMusic": false,
"IOObstaclSlowFac": 0.5,
"ManualSpeed": 0.2,
"ManualRotateSpeed": 10.0,
"GamepadRotateSpeed": 1.0,
"RecoveryTime": 2000.0,
"ManualModeArea": 0,
"ManualModeIOArea": 1,
"UseLessTag": true,
"MaxAngularSpeed": 75.0,
"SafetyRelayShielded": false,
"SafetyRelayShieldedRleaseTime": 30,
"RotateIoArea": 16,
"ChargeMaxThreshold": 95.0,
"TransmitterCOMNum": "COM5",
"UseTransmitter": true
}
-50
View File
@@ -1,50 +0,0 @@
{
"WheelConfig": {
"LeftFront": {
"Position": {
"X": 525.0,
"Y": 200.0
},
"WheelDistance": 85.0,
"AngleLowerLimit": -128.0,
"AngleUpperLimit": 172.0,
"IsDiffWheel": true
},
"RightFront": {
"Position": {
"X": 525.0,
"Y": -200.0
},
"WheelDistance": 85.0,
"AngleLowerLimit": -128.0,
"AngleUpperLimit": 172.0,
"IsDiffWheel": true
},
"LeftRear": {
"Position": {
"X": -525.0,
"Y": 200.0
},
"WheelDistance": 85.0,
"AngleLowerLimit": -126.0,
"AngleUpperLimit": 174.0,
"IsDiffWheel": true
},
"RightRear": {
"Position": {
"X": -525.0,
"Y": -200.0
},
"WheelDistance": 85.0,
"AngleLowerLimit": -130.0,
"AngleUpperLimit": 170.0,
"IsDiffWheel": true
}
},
"MinimumTurningAngleForAckermann": 60.0,
"ControlPointRadius": 500.0,
"MaxSpeed": 1.0,
"AccPerSecond": 0.3,
"DeAccPerSecond": 1.0,
"MinTurnSpeedFac": 0.25
}
-534
View File
@@ -1,534 +0,0 @@
{
"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
}
+116
View File
@@ -0,0 +1,116 @@
# 技术决策
只记录长期有效的方案状态;实验临时参数和失败尝试不在此保存。
## 已经实施
### 1. 分离 CommonUsage、Shared、M层和C层
- `CommonUsage` 保持独立通用底盘库,不反向依赖项目控制层。
- `Shared` 只保存M/C共享模型、数学、校验和底盘适配,不建立独立程序集。
- `MedullaAdapter` 管硬件,`MultiWheelC` 管动作和控制。
- 原因:避免硬件通信、控制算法和旧底盘库相互反向耦合。
- 依据:三个 `.csproj``AGENTS.md`
### 2. Shared统一SI单位和真实车体系命令
- Shared使用m、m/s、rad、rad/s,车体系X前Y左、逆时针为正。
- 对外底盘命令统一为 `Twist2D`;旧版mm/deg接口只在 `MultiWheelChassisAdapter` 边界转换。
- 原因:减少不同层之间的单位和符号歧义,为后续单车/车队刚体速度分配保留统一模型。
### 3. 显式β运动坐标系,不根据速度分量猜测模式
- 切换流程固定为停车、预对齐舵轮、`ActivateMotionFrame(β)`,运行阶段统一调用 `SendBodyTwist()`
- β=0表示车头方向,β=90°表示车体左侧作为虚拟前向;支持任意固定β。
- 实际四轮始终使用真实布局参与解算,β只改变表达坐标系。
- β不是改变目标刚体运动的额外自由度;它用于为同一 `BodyTwist` 选择本运动段更易满足舵角、最小转舵和非奇异条件的主要滚动方向。
- β不增加车辆物理运动能力,也不改变同一刚体速度最终要求的轮子滚动轴线;当前主要价值是让真实车体系 `BodyTwist` 能稳定接入方向型旧 `SendMotion`,并使虚拟GCP角远离±90°表达奇异区。若以后改为 `BodyTwist` 直接到四轮的完整逆解,β可以退化为底盘内部的等效解选择,不应成为车队控制器的核心概念。
- β可参与运动前的候选方向规划,但不能代替真实舵轮限位检查:±120°保护必须对完整四轮解算结果逐轮检查,并同时考虑“舵角±180°、轮速反向”的等效解。
- 限制:运动中不切换β;纯自转要求回到真实车体系并采用专用舵轮准备。
- 依据:`PrepareWheelsForward``TrajectoryTrackingMovement``MultiWheelChassisAdapter`
### 4. 保留前后GCP作为旧底盘几何边界
- 控制层输出前后虚拟GCP方向;`MultiWheelChassis.SendMotion` 由两条法线求ICR,再计算每个真实轮子的切线方向和速度比例。
- GCP是刚体控制点,不是物理轮轴中心;`ControlPointRadius` 必须在前馈、运动学和旧解算中保持一致。
- 原因:复用已经存在的机械限位、等效舵角、速度斜坡和差速舵轮分配逻辑。
### 5. 轨迹采用弧长参数化而非时间参数化
- 轨迹保存点序、累计弧长、车体中心位姿、曲率和有符号参考速度。
- 实际周期 `deltaTime` 只进入控制器、滤波器和命令变化率,不作为轨迹索引。
- 原因:执行进度由车辆空间位置决定,调度周期波动不会直接跳过时间采样点。
### 6. 正负参考速度表达前进和倒车
- 轨迹Yaw始终是车头方向;正速度前进、负速度倒车。
- Stanley曲率前馈、横向/航向修正和终点行驶方向显式考虑速度符号。
- 同一测试曲线要求速度同号,不在滚动中直接切换前进/倒车。
- 依据:`TrajectoryPoint``TestTrajectoryFactory``StanleyLateralController`
### 7. 默认状态采用Detour位姿与轮组平面速度组合
- 位姿和Omega来自经过校验/滤波的Detour。
- 车体系Vx、Vy来自 `GetCarSpeed(true)`,分别使用同一时间常数低通滤波。
- 轮组Vw同样滤波,但仅用于Detour跳变期间的短时位姿预测、运动合理性和动态航向创新阈值,不替换控制输出中的Detour Omega。
- 原因:位置依赖SLAM,控制平面速度优先使用响应更直接的电机/舵角反馈;轮组Vw对短时运动趋势有用,但动态精度尚不足以直接作为闭环角速度。
- 依据:`ParkingVehicleStateProviderFactory``WheelFeedbackVehicleStateProvider`
### 8. 横向控制可替换,纵向控制暂保持PID
- `ILateralController` 是Stanley/LQR/MPC等的稳定扩展点;`TrajectoryTrackingMovement.LateralControllerFactory` 可注入实现。
- 默认横向为Stanley,默认纵向为 `PidLongitudinalController`
- 当前没有单独的控制器工厂文件,避免为手动替换增加不必要结构。
### 9. 低速和终点采用单向收敛
- 起步区域使用小幅释放速度;终点前读取更低速度并进入单向低速逼近。
- 到达或越过终点后只停车,不生成反向修正速度。
- 完成同时检查剩余弧长、终点欧氏距离、车头航向和β方向实际速度。
- 原因:停车机器人终点附近反复前后修正风险高,优先保证运动方向稳定和可预测。
- 依据:`ParkingGeometricController.ResolveTerminalApproachSpeed()``HasReachedEnd()`
### 10. 对已观察的舵轮响应加入有限前馈与预瞄
- M层差速转舵角速度前馈已接入,当前默认增益0.9、速度上限0.03m/s;实现位于 `MotorRoutine.CalculateDiffSteerRateFeedforward()`
- Stanley曲率前馈预瞄已接入,当前车辆默认时间0.15s、最大距离0.12m。
- 两者均是可配置补偿,不替代底层PID、真实周期和机械响应验证。
### 11. 车辆级参数集中到 PilotConfig,动作只做必要覆盖
- 停车控制参数集中在 `Configuration/PilotConfig.ParkingControl.cs`,动作默认读取 `PilotDefinition.Conf`
- 组合运动段可以覆盖自身完成条件;实验轨迹速度描述实验本身,车辆级最大命令速度仍负责最终限制。
### 12. Detour阶跃采用任务坐标连续化与安全停车分级处理
- Detour原始世界观测与当前任务控制位姿通过 `controlFromDetour` 隔离;疑似阶跃先进入候选确认,确认期间使用轮组 `Vx/Vy/Vw` 短时预测。
- 仅对幅值受限且连续确认的小坐标偏移自动更新变换;大幅、不一致或超过确认窗口的变化使状态不可用,由动作安全停车。
- 完整轨迹状态继续要求位置和航向均可靠;原地自转只依赖独立验证的航向,位置单独异常不再终止正在进行的自转,航向异常仍触发安全停止。
- 近似原地自转期间禁止自动吸收坐标偏移,避免把旋转定位劣化写入任务坐标系;目标航向到达并停止驱动后,才在既有幅值和确认窗口内处理位置恢复,未恢复时禁止进入下一运动段。
- `l_step` 当前只作为诊断和后续健康分级依据,不单独决定状态有效性。
- 依据:`DetourVehicleStateProvider``WheelFeedbackVehicleStateProvider``MultiWheelRotateInPlace`
## 已经确认但尚未实施
- 路线顺序:先完成单车闭环和停车功能验证,再正式实施多车通信、编队和协同控制。来源:`README.md`
- 当前代码没有可以确认的完整多车分配方案:`Shared/Fleet/FleetKinematics.cs` 仍只有职责注释,`VehicleLayout``FleetMotionCommand` 目前只有模型定义、没有业务调用;不得把这些类型当成已经实现的功能。
- 多车共同搬运不能只闭环车队中心:整体位姿误差与成员相对布局误差必须分开估计和约束,否则成员误差可能相互抵消而使平均中心看似正确。
- 计划采用分层职责:车队控制器产生参考点 `FleetTwist`,分配层依据成员 `VehicleLayout` 计算每车真实车体系 `BodyTwist`,单车层继续负责β变换、GCP和本车四轮解算。
- 虚拟车队可使用固定在车队坐标系中的前后GCP作为控制几何,例如位于 `±L_F`;这些点只用于把横向控制结果转换成车队参考点 `FleetTwist`,不是物理轮轴,也不直接参与单车四轮解算。前后GCP方向仍随控制输出动态变化,且横向控制器与GCP到Twist转换必须使用同一 `L_F`。该方案尚未实施。
- 每辆成员车都应作为反馈来源,但反馈职责必须分层:成员Detour位姿用于融合车队整体位姿和检查相对布局,单车轮速/舵角用于确认命令执行偏差,电机电流、扭矩或力传感信息用于负载与内力监控。相对位姿接近目标并不能证明没有内力,因此不能只依靠刚性连接或位姿误差判断负载均衡。
- 每车β应由车队分配层基于整个运动段的速度方向、机械舵角和可行性集中选择;只允许在全车停车时准备和激活。所有成员确认舵轮到位后通过车队级同步屏障统一释放非零运动命令。
- 旧版参考项目采用固定双车布局:各车由 `carWorld ∘ layout⁻¹` 反推车队中心,再对位置和圆周航向求平均;路径控制器以该虚拟中心跟踪轨迹。同时它可按 `fleetTarget ∘ layout_i` 生成每车理想位姿,并叠加Detour布局纠偏和邻车两腿检测纠偏,因此并非只控制平均中心。来源:`原版停车机器人/parkingrobot/ClumsyPilot/PilotDefinition.cs``ChassisController.cs`
- 旧版 `SetOriginBias(layoutX, layoutY, layoutTh)` 是把各车真实轮子统一表达在车队虚拟坐标系中,属于固定编队布局变换;蟹行通过共同 `frontTh/rearTh` 表达。它与新版“每车按运动段独立选择纯旋转β”的职责不同,虽然两者可产生相同的实际轮子姿态和车辆运动。
- 旧版自动 `FleetCurveWalk``FleetCrabWalk` 会先以零速度下发初始GCP角,等待成员新鲜、布局正确、命令可行、舵轮到位和从车应用新序列后才开始运动;原地旋转通过 `RotateWheelsAligned``FleetMotionReleased` 做整队释放。普通手动入口仍有 `SendMotion` 本车舵轮未对齐时速度置零的门控,但不保证与自动动作相同的车队级同步屏障。
- 旧版的全局与局部定位用途不同:自动模式的车队中心估计仍依赖Detour,与局部POS纠偏开关无关;邻车两腿检测只能提供相对间距和姿态信息,不能单独确定世界坐标中的车队中心。
- 旧版单车几何控制器和原地自转直接读取 `DetourInterface.getCartLocation()`,未见当前新版的源时间对齐、跳变候选或任务坐标连续化;旧版宿主参考代码另有基于 `l_step` 和数据新鲜度减速/暂停并触发重定位的 `RelocalizationManager`。这只能说明旧版通过宿主安全状态机管理明显失效,不能据此认定Detour位姿天然连续;实车部署是否使用相同宿主版本和配置仍待确认。来源:`mdcstoolbox/Clumsy/MotionControllers/AbstractGeometricController.cs``原版停车机器人/parkingrobot/ClumsyPilot/Movements.cs``mdcstoolbox/Clumsy/HighLevelSecurity/RelocalizationManager.cs`
- 旧版的 `MultiVehicleUseDetect``MultiVehicleSyncUseDetour` 是彼此独立且默认关闭的开关,并非强制二选一。两者同时开启时,常规/蟹行的 `SendMotion()` 会直接将检测补偿与POS补偿逐分量相加,没有互斥或状态级融合;这不等于每次运行必然冲突,但存在重复修正、相互对抗和放大噪声的结构风险。来源:`原版停车机器人/parkingrobot/ClumsyPilot/PilotConfig.cs``PilotDefinition.cs`
- 旧版方案只作为设计参考,不直接移植:`GetLayoutPoseForCar()` 将成员写死为两侧镜像布局,车队几何通过 `SetOriginBias(layout)``ControlPointRadius=distance/2` 进入各车底盘解算;当两类局部纠偏均关闭时,成员一致性仍可能依赖共同开环命令和被搬运物的机械约束。
## 待评估
- 多车共同搬运时的车队参考点与固定GCP距离、任意成员布局、加权/异常值鲁棒的车队位姿估计、队形误差闭环,以及 `FleetMotionCommand → 每车Twist2D` 的具体分配与限幅算法。
- 负载共享和内力监控可用信号、阈值、降级与停车策略;当前项目尚未建立可确认的力/扭矩闭环。
- 正式轨迹规划层与 `Trajectory2D` 的接入格式;当前 `TestTrajectoryFactory` 仅用于实验。
- 是否长期保留旧版GCP/`SendMotion`后端,或增加经过充分验证的“车体Twist直接到各轮”的新后端。
- 蟹行遥控中按半轮距/半轴距缩放舵角的映射是否符合统一曲率语义;这是遥控手感策略,不应与坐标变换混为一谈。
+44
View File
@@ -0,0 +1,44 @@
# Detour 信息确认清单
用途:向 Detour 负责人确认控制程序使用定位结果所必需的信息。当前不需要 Detour 源码,接口说明或书面答复即可。
## 1. `getCartLocation()` 字段定义
- `x``y``theta``tick``l_step` 分别表示什么。
- 各字段的单位、正方向和坐标系。
- `theta` 的取值范围,例如 `[-180°, 180°]`
- 一次调用返回的字段是否属于同一个定位帧。
## 2. `tick` 的准确含义
- 是传感器采集时刻、SLAM 计算时刻,还是接口返回时刻。
- 单位、更新频率、是否单调递增,以及是否可能重置或回绕。
- 多台车的 `tick` 是否来自同步时钟,能否用于多车位姿时间对齐。
## 3. 定位延迟与更新方式
- 输出位姿通常比真实运动滞后多少毫秒。
- `getCartLocation()` 是否返回最近一次缓存结果。
- 原地自转时更新频率或延迟是否会明显变化。
## 4. `l_step` 的含义
- 它表示优化迭代次数、定位质量、匹配状态,还是其他指标。
- 为什么原地自转时可能从 2~4 上升到几十甚至上百。
- 是否有官方推荐的正常、退化和不可用阈值。
## 5. 坐标跳变与重定位行为
- 重定位、回环优化或匹配失败恢复后,`x/y/theta` 是否可能永久跳变。
- 跳变后是否仍处于原来的地图坐标系。
- 是否存在“正在重定位”“定位丢失”“地图坐标调整”等状态标志。
## 6. 可用的定位质量接口
-`l_step` 外,能否获得匹配得分、协方差、置信度、定位状态或错误码。
- Detour 官方建议控制程序依据哪些条件接受或拒绝一帧位姿。
## 优先级
最优先确认第 2、4、5、6 项。拿到这些信息后,可确定 `MultiWheelC/StateEstimation` 中时间同步、跳变连续化、自转短时预测和安全停止规则的最终设置。
-365
View File
@@ -1,365 +0,0 @@
需要处理,而且对横向误差、航向误差和速度估计都会有明显影响。检测本身不难,困难的是区分:
```text
单帧错误定位
持续性的定位重定位/地图修正
车辆真实的快速运动
```
对你这种低速停车机器人,可以先采用一套偏安全、容易验证的处理。
## 定位跳变会造成什么影响
假设Detour在50ms内突然跳了10cm
\[
v=\frac{0.1}{0.05}=2m/s
\]
实际车辆可能只有 `0.3m/s`,但差分速度会产生 `2m/s` 的尖峰。
对控制器还有三个直接影响:
- 横向位置跳10cm,横向误差可能瞬间变化10cm。
- 航向跳5°,航向误差会瞬间变化5°。
- 全局轨迹投影可能跳到另一条临近或相交的轨迹线段。
Stanley在低速时尤其敏感:
\[
\delta =
e_\theta+
\arctan\left(\frac{k e_y}{v+\varepsilon}\right)
\]
低速时分母较小,横向误差突然变大,会产生很大的转向指令。因此不能完全不处理。
## 不建议直接低通掉定位跳变
不要简单地对Detour位置做强低通:
```text
错误位置跳变
→ 低通缓慢跟过去
```
这样虽然曲线看起来平滑,但控制器会在一段时间内使用滞后、虚构的位置,可能更加危险。
更好的做法是:
```text
检测跳变
→ 暂时不把该帧用于速度差分和控制
→ 观察后续定位
→ 判断是单帧异常还是持续重定位
```
## 第一层:运动学合理性检查
将当前Detour位姿和上一次接受的位姿比较。
位置变化:
\[
\Delta p=\sqrt{\Delta x^2+\Delta y^2}
\]
航向变化:
\[
\Delta\theta=
|\operatorname{ShortestAngleDifference}|
\]
允许的最大变化量可以按照车辆物理能力计算:
```csharp
var maximumAllowedDistance =
maximumLinearSpeedMetersPerSecond *
deltaTimeSeconds +
positionJumpMarginMeters;
var maximumAllowedHeadingChange =
maximumAngularSpeedRadiansPerSecond *
deltaTimeSeconds +
headingJumpMarginRadians;
```
然后判断:
```csharp
var positionJump =
displacementMeters >
maximumAllowedDistance;
var headingJump =
headingChangeRadians >
maximumAllowedHeadingChange;
```
你当前小车最高约 `1.2m/s`,假设Detour更新周期为50ms
```text
物理最大位移约为:
1.2 × 0.05 = 0.06m
```
初期可以额外留出约 `0.03~0.05m` 的定位余量。不过这些阈值最终应根据实际Detour数据确定,不建议永久写死。
## 第二层:不要立即接受异常帧
检测到一个异常帧时,不要立刻改变车辆状态:
```text
上一正常位置 A
异常位置 B
下一帧又回到 A 附近
```
这种情况说明B很可能是单帧异常,应直接丢弃。
如果后续连续若干帧都稳定在B附近:
```text
A → B → B附近 → B附近
```
这更可能是Detour发生了持续性的重定位。
可以使用:
```text
连续23个新定位帧相互一致
```
作为重新接受定位的条件。
## 第三层:重定位后必须重置速度估计
如果确认新的定位是持续有效的,不能用:
```text
新位置B - 旧位置A
```
计算速度,因为A到B是定位修正,不是车辆真实运动。
正确处理是:
```csharp
_velocityEstimator.Reset(
newPose,
currentTimestamp);
```
这会:
- 将新位姿作为新的差分起点。
- 清除之前的速度历史。
- 重置三个低通滤波器。
- 将速度暂时标记为无效或零。
- 等下一次正常Detour更新后重新开始估计。
## 第四层:控制器应该如何响应
对于实车轨迹跟踪,建议状态分为:
```text
Valid 正常定位,可以控制
Suspected 检测到疑似跳变
Reacquiring 正在确认新的定位
Stale 定位长时间没有更新
```
你的第一版不一定需要单独增加复杂枚举,但控制行为至少应该满足:
```text
正常:
继续轨迹跟踪
疑似单帧跳变:
不使用异常帧更新状态
短时间保持上一状态
连续异常或定位超时:
停车,不继续使用旧状态运动
确认重定位:
接受新位姿
重置速度估计
重新执行轨迹投影
确认稳定后恢复控制
```
停车机器人速度低、场地有限,定位连续异常时停车比盲目继续跟踪更合适。
## 还需要限制轨迹投影进度
即使Detour跳变检测做了,轨迹投影也最好增加进度保护。
当前投影器在整条轨迹上找最近点,如果轨迹自交,车辆可能从:
```text
当前第20段
```
突然投影到:
```text
第80段
```
后续可以增加一个 `TrajectoryProgressTracker`
```text
正常时只在上次线段索引附近搜索
例如 [previousIndex - 5, previousIndex + 20]
定位重定位后:
重新执行一次全局搜索
```
同时限制单周期允许的弧长进度变化:
\[
|\Delta s|
\leq
v_{\max}\Delta t+\text{margin}
\]
这样可以避免定位轻微波动导致轨迹进度突然跳段。
## 在五个文件中的分工
```text
DetourVehicleStateProvider
├─ 检查数值有效性
├─ 检查定位是否过期
├─ 检测重复帧
├─ 检测位置/航向跳变
├─ 暂存疑似新定位
└─ 确认重定位后触发Reset
VelocityEstimator2D
├─ 正常位姿差分
├─ 真实时间差计算
├─ 速度滤波
└─ 提供Reset()
FirstOrderLowPassFilter
└─ 提供Update()和Reset()
VehicleState
├─ 保存位姿和速度
└─ 表示速度估计是否有效
IVehicleStateProvider
└─ 向控制器返回有效状态或读取失败
```
因此定位跳变的主要逻辑放在 `DetourVehicleStateProvider`,而不是低通滤波器。
第一版建议先实现:
1. 非有限值检查。
2. 重复帧识别。
3. 基于最大速度和最大角速度的跳变检测。
4. 单帧异常拒绝。
5. 连续23帧确认重定位。
6. 重定位后重置速度估计。
7. 长时间定位无效时通知控制器停车。
这套方案足以保护当前Stanley和后续纵向控制,不需要现在就修改SLAM源码。
已经给 [DetourVehicleStateProvider.cs](/D:/Users/Desktop/入职培训/停车机器人/MyParking/MultiWheelC/StateEstimation/DetourVehicleStateProvider.cs:233) 增加了基于上一有效速度的预测残差检测。
现在采用两级判断:
```text
第一级:绝对物理运动边界
第二级:上一状态恒速预测残差
```
预测公式为:
\[
x_{pred}=x_{last}+v_x\Delta t
\]
\[
y_{pred}=y_{last}+v_y\Delta t
\]
\[
\theta_{pred}=
\theta_{last}+\omega\Delta t
\]
实测残差为:
\[
e_p=
\sqrt{
(x_{measured}-x_{pred})^2+
(y_{measured}-y_{pred})^2
}
\]
以及:
\[
e_\theta=
\operatorname{ShortestDifference}
(\theta_{measured},\theta_{pred})
\]
对应实现在:
- [IsPredictionResidualPlausible()](/D:/Users/Desktop/入职培训/停车机器人/MyParking/MultiWheelC/StateEstimation/DetourVehicleStateProvider.cs:536)
- [PredictPoseInWorld()](/D:/Users/Desktop/入职培训/停车机器人/MyParking/MultiWheelC/StateEstimation/DetourVehicleStateProvider.cs:573)
默认阈值为:
```csharp
位置预测残差:0.04m
航向预测残差:5°
```
没有采用最初考虑的30mm,是因为用现有数据离线统计后发现:
| 位置残差阈值 | 触发次数 | 占有效更新比例 |
|---:|---:|---:|
| 30mm | 55次 | 1.24% |
| 35mm | 28次 | 0.63% |
| 40mm | 13次 | 0.29% |
| 45mm | 7次 | 0.16% |
| 50mm | 4次 | 0.09% |
30mm容易把正常Detour波动也判成异常;40mm仍能识别现有数据中约49mm和66mm的明显跳变,同时误触发风险更低。
当前判定逻辑:
```csharp
if (!isWithinPhysicalBoundary ||
!isWithinPredictionResidual)
{
// 进入疑似重定位确认状态。
}
```
另外同步修正了跳变恢复逻辑:
- 发生预测残差跳变时,保存当时的预测位姿。
- 后续定位必须回到预测位姿附近,才能认为是单帧异常后的正常恢复。
- 不再使用较宽松的绝对物理范围立即放行。
- 持续远端定位仍需满足3次观测和0.25秒,才按重定位接管。
- 接管后重置速度估计,避免把坐标修正计算成车辆速度。
第一帧或重定位后的速度还没有建立时,不启用预测残差检测,只使用物理极限检查,避免没有速度基准时误判。
完整构建结果:
- CommonUsage0警告、0错误
- MedullaAdapter0警告、0错误
- MultiWheelC0警告、0错误
+184
View File
@@ -0,0 +1,184 @@
# 关键接口与数据约定
## 坐标系和单位
`Shared/` 的统一约定:
- 位置:m;线速度:m/s;角度:rad;角速度:rad/s。
- 真实车体坐标系:X向前、Y向左、逆时针为正。
- 原始世界坐标来自Detour;位姿在 `DetourVehicleStateProvider.ReadDetourObservation()` 边界由mm/deg转换为m/rad,再通过任务坐标变换生成连续控制位姿。
- `AngleMath` 的弧度归一化范围是 `[-π, π)`,角度范围是 `[-180°, 180°)`
- `Pose2D` 表示局部坐标系在父坐标系中的位姿;变量名使用 `XxxInYyy` 说明关系。
- `Twist2D` 不携带坐标系标签,必须由变量名、外层类型或接口契约说明。
旧版 `CommonUsage` 底盘接口使用混合单位:轮子位置和 `ControlPointRadius` 为mm`SendMotion` 舵角与 `CarSpeed.Vw` 为deg/deg/s,线速度为m/s。单位转换应只出现在Shared适配边界。
## Shared 数据模型
文件:`Shared/Models/MotionModels.cs`
| 类型 | 语义 |
| --- | --- |
| `Point2D` | 二维位置或向量,单位m |
| `Pose2D` | 二维位置和朝向,单位m/rad |
| `Twist2D` | 同一点处的 `Vx``Vy``Omega`,单位m/s、rad/s |
| `VehicleLayout` | 单车车体系在车队系中的位姿;当前仅数据模型 |
| `FleetMotionCommand` | 车队参考点及该点速度;当前尚无车队分配执行器 |
坐标变换集中在 `FrameTransform2D`;有限值检查集中在 `NumericGuard`;角度处理集中在 `AngleMath`
## 轨迹契约
文件:`MultiWheelC/Trajectory/`
### `TrajectoryPoint`
- `ArcLengthMeters`:按预定执行点序从起点累计的弧长,首点必须为0。
- `PoseInWorld`:车体中心参考位姿;Yaw始终表示车头方向,不因倒车改为车尾方向。
- `CurvaturePerMeter`:沿弧长增加/执行点序定义,左弯为正。
- `ReferenceSpeedMetersPerSecond`:轨迹切线方向的有符号车体中心参考平移速度,绝对值是速度模长,正值前进、负值倒车、0停车。
### `Trajectory2D`
- 至少两个点,弧长严格递增,相邻位置不能重合。
- 弧长增量必须与离散线段长度在容差内一致。
- 当前是空间轨迹,以弧长插值位置、Yaw、曲率和参考速度;没有时间戳,也不是时间参数化轨迹。
- `SampleAtArcLength()``TrajectoryProjector` 共用 `InterpolateSegment()`,避免两套插值语义。
### `TrajectoryProjector`
- 首周期可全轨迹搜索;后续控制器使用上次弧长附近窗口,当前常量为后退0.10m、前进1.00m。
- 距离并列时优先接近上次进度,降低交叉或平行轨迹跳段风险。
- `LateralErrorMeters` 相对轨迹点序判断左右,轨迹位于车辆左侧时为正。
- `HeadingErrorRadians` 是参考车头航向减实际车头航向的最短角差。
测试轨迹由 `Experiments/TestTrajectoryFactory.cs` 生成,不是正式规划层。直线和曲线速度同号;负速度表示倒车,测试工厂不支持在同一条曲线中直接切换前进/倒车方向。
## 状态接口
### `DetourInterface.getCartLocation()`
当前引用接口返回的定位对象包含:
- `x``y`Detour世界坐标,单位mm。
- `th`:车体航向,单位deg。
- `tick`:定位源时间;实车静态记录确认可按`.NET DateTime.Ticks`转换,同时必须保留原始整数用于诊断。
- `l_step`:Detour定位过程的质量/步骤相关指标,精确定义和正式阈值待Detour文档确认;实车正常静态基线包含2和周期性单帧3,不能将3直接判为异常。
`DetourVehicleStateProvider` 使用 `x/y/th` 生成控制位姿,使用 `tick` 区分重复、新到和倒退帧,并保留 `l_step` 供诊断。`l_step` 当前不作为单一硬门限:实车数据中既出现过高 `l_step` 后恢复,也出现过低 `l_step` 但位姿创新异常的情况。状态时间仍使用本机单调 `Stopwatch``Experiments/DetourStaticDiagnosticTest.cs` 可只读记录原始字段、接口耗时、帧间差和轮组反馈。
当前继续设计状态层所需的Detour侧最小信息是:`getCartLocation()` 返回的是可能重定位跳变的全局/map位姿还是连续里程计位姿;`tick` 对应采集、解算还是发布时刻及缓存语义;`l_step` 的状态含义;现有接口是否另有连续里程计位姿/速度或定位有效、重定位状态。无需以取得Detour源码为前提,部署版本、里程计和单线激光SLAM配置截图可用于核对实际运行配置。以上信息尚待Detour侧确认。
### `IVehicleStateProvider`
```text
bool TryGetState(out VehicleState state)
```
返回 `false` 表示当前状态不可用;`ParkingGeometricController` 会主动停车、重置反馈控制器并等待下一周期恢复。
### `VehicleState`
- `SampleTimestampSeconds`:状态源单调时钟时间。
- `PoseInWorld`:当前任务控制世界系中的车体中心位姿;初始化时与Detour世界系对齐,确认有限坐标阶跃后可通过内部变换保持任务连续,不保证始终等于原始Detour坐标。
- `TwistInWorld``TwistInBody`:同一刚体速度的两种表达。
- `HasValidVelocityEstimate`:速度反馈是否已建立有效时间基准;无效时构造器将速度置零。
### 默认组合状态源
`ParkingVehicleStateProviderFactory.Create()` 创建:
- `DetourVehicleStateProvider`:读取Detour位姿,处理源时间、重复帧、运动合理性、预测创新、静止确认和跳变候选;候选确认期间使用轮组速度短时预测控制位姿。确认后的有限小坐标偏移可更新 `controlFromDetour` 以保持当前任务坐标连续,超限或未恢复时将状态置为不可用。
- `WheelFeedbackVehicleStateProvider`:调用 `MultiWheelChassis.GetCarSpeed(true)`,低通滤波车体 `Vx``Vy` 和由deg/s转换为rad/s的 `Vw`。最终 `VehicleState` 的平面速度使用轮组 `Vx/Vy`Omega仍使用Detour估计;轮组 `Vw` 只提供给Detour短时运动预测和动态航向合理性判断。
默认滤波参数来自 `PilotConfig.ParkingControl.cs`Detour线速度0.15s、Detour角速度0.20s、轮组反馈 `Vx/Vy/Vw` 统一为0.10s。
跳变处理的重要边界:航向创新允许量随 `|Vw| × Detour源帧间隔` 增加;候选若在近似原地自转期间开始,则整个候选确认过程禁止自动改写任务坐标系。默认候选确认窗口为0.60s,窗口内输出轮组预测状态,超时后完整位姿安全返回不可用。
完整轨迹控制仍通过 `TryGetState()` 要求位置和航向均有效。原地自转则由 `WheelFeedbackVehicleStateProvider.TryGetHeadingRadians()` 获取独立可靠航向:仅位置异常时可进入 `PositionUnavailableHeadingAvailable`,不会中断正在进行的航向闭环;航向异常仍会停止动作。到达目标并停止驱动后,`MultiWheelRotateInPlace` 调用 `BeginPostRotationPositionRecovery()`,在原有3帧、0.60s及最大自动平移/航向偏移限制内恢复完整位姿,恢复失败时不释放下一运动段。
`TrackingExperimentRecorder` 已记录原始/滤波轮组 `Vx/Vy/Vw`、轮组采样时刻、Detour `tick/l_step`、数据年龄和源帧间隔、轮速预测时刻、实际/允许的位置与航向创新、候选触发原因、估计器状态及不可用原因。创新和源帧间隔只在收到Detour新帧时更新;若CSV记录频率高于Detour帧率,后续记录行会重复最近一个新帧的诊断值,分析时应按 `DetourTickRaw` 去重或分组。
## 控制接口
### `PathTrackingContext`
一次控制周期的只读快照,包含:`VehicleState``TrajectoryProjection`、处理后的控制参考速度、预瞄曲率、真实 `deltaTime` 和运动方向β。
`ActualLongitudinalSpeedMetersPerSecond` 是完整车体平面速度沿β方向的投影:
```text
Vβ = cos(β)·Vx_body + sin(β)·Vy_body
```
因此45°/90°蟹行不能只使用车体 `Vx` 判断纵向速度。
当前速度闭环和执行边界的语义并不完全相同:纵向PID与Stanley实际速度分母使用 `Vβ`(Stanley也可按配置改用参考速度),而 `GcpMotionCommand.SpeedMetersPerSecond` 和旧版 `SendMotion.speed` 表示带行驶方向符号的车体中心平移速度模长。正常圆弧理想跟踪时 `Vy=0`,两者相等;只有横向误差共同转角产生非零 `Vy` 时,模长与 `Vβ` 才相差余弦因子。当前最大命令速度仍限制最终发送的模长。
### `ILateralController`
- `Compute(PathTrackingContext)` 返回 `LateralControlCommand`
- `Reset()` 清除跨周期状态。
- 默认实现 `StanleyLateralController` 输出前后GCP角度:横向误差形成共同转角,航向误差和曲率前馈形成差动转角;倒车时按行驶方向修正符号。
- `TrajectoryTrackingMovement.LateralControllerFactory` 是替换Stanley的动作级扩展点。
### `ILongitudinalController`
- `ComputeSpeedMetersPerSecond(PathTrackingContext)` 返回有符号中心命令速度。
- 默认 `PidLongitudinalController` 使用轨迹参考速度前馈叠加实际速度PID反馈,带死区、积分限制和最大命令速度;参考明确为0时禁止反向速度纠偏。
### GCP命令
- `LateralControlCommand`:前后GCP目标角度,以及共同/差动分量。
- `GcpCommandAllocator`:分别限制前后GCP角度并与纵向速度组合。
- `GcpMotionCommand`:进入底盘执行前的有符号速度和前后GCP角度,SI单位。
- `GcpCommandExecutor`:限制GCP角速度,保存 `LastRequestedCommand`/`LastSentCommand`,转换为 `Twist2D` 后执行。
对称前后GCP位于当前运动系的 `(±R, 0)`。依据刚体速度关系 `v(point)=v(center)+ω×r`
```text
Vfront = (Vx, Vy + ωR)
Vrear = (Vx, Vy - ωR)
```
因此 `Vy` 形成前后同向的共同转角,`ωR` 形成前后反向的差动转角。正常圆弧的理想车体速度为 `(v, 0, v·κ)`,曲率通过差动转角实现,并不要求车体系存在 `Vy`
## 底盘命令接口
### `MultiWheelChassisAdapter.SendBodyTwist`
输入始终是真实车体系 `Twist2D`
- 平移和角速度均近零:立即停车。
- 平移近零、角速度非零:要求真实车体系已激活,调用 `SendXYThSpeed` 做纯自转。
- 平移非零:使用动作开始前已经准备并激活的固定β运动系,转换为前后GCP方向并调用旧版 `SendMotion`
运动中不通过 `Vx/Vy` 猜测模式;模式由“停车→舵轮预对齐→`ActivateMotionFrame(β)`”显式确定。命令若几乎垂直于当前运动系X轴会停车并拒绝执行。
`MultiWheelChassisAdapter` 构造时读取旧底盘 `GetOriginBias().Z`,按 `β=-biasZ` 同步内部缓存;这不是SLAM Yaw,也不会切换坐标系或转动舵轮。真正切换由 `ActivateMotionFrame(β)` 调用 `SetOriginBias(0, 0, -β)` 完成,旧底盘随后把真实轮位重新表达在β运动系中。
滚动命令在 `SendRollingTwistInActiveMotionFrame()` 中按 `R(-β)` 将真实车体系平面速度转换到已激活运动系;角速度在二维旋转变换下不变。传给 `SendMotion` 的前后GCP角度均相对该运动系X轴,速度绝对值为 `sqrt(Vx²+Vy²)`,正负号取运动系 `Vx` 的方向。倒车方向由速度符号表达,GCP角度保持为相对行驶方向的等效机械方向,避免无意义旋转180°。
### `MultiWheelChassis.SendMotion`
输入是车体中心有符号平移速度模长、当前运动系中的虚拟前后GCP方向。前后GCP法线交点确定ICR;每个真实轮子使用其运动系位置计算切线舵角和半径速度比例。机械限位通过等效舵角/反向轮速解析,无法满足时返回失败原因。
## 配置接口
- `PilotConfig` 是Clumsy运行配置模型;`[FieldMember]` 字段提供默认值和宿主显示/持久化元数据。
- `PilotDefinition.Conf` 是C层动作实际读取的运行配置对象。
- `MultiWheelC/Configuration/PilotConfig.ParkingControl.cs` 集中停车状态估计、Stanley、纵向PID、原地自转、舵轮准备、GCP与完成条件参数。
- 动作中的 nullable 覆盖字段用于特定动作段或测试;为空时使用车辆配置。车辆级限速和通用控制参数不应在普通实验中随意覆盖。
- `参考文档/*.json` 是样例/实车复制资料;当前源码未发现这些JSON被 `PilotDefinition.Conf` 自动读取的入口。
运行时配置文件格式、位置和覆盖优先级由外部Clumsy宿主决定,当前仓库内待确认。
## M/C IO与MCU边界
- C层 `PilotDefinition` 和M层 `DiverCartDefinition` 使用 `[AsUpperIO]`/`[AsLowerIO]` 对齐夹臂命令、驱动使能、位置反馈和车号等字段。
- `DiverCartDefinition.CommunicationInit()` 默认通过Windows `COM4``1,000,000 baud` 打开MCU桥;`MCUPort` 是可配置初始化参数。
- MCU内部配置为逻辑端口0CAN `500,000 bit/s`;逻辑端口13:串口 `9,600 bit/s``MCURoutine.BatteryPortIndex = 3` 指MCU桥逻辑端口,不等同于Windows `COM3`
- CAN命令/反馈范围集中在 `MCURoutine.cs`:驱动命令 `0x2010x20A`,速度/位置反馈 `0x2810x28A`,状态 `0x1810x18A`,舵角 `0x18B0x18E`,远程帧 `0x7010x70A`
- `MCUSerialBridgeCLR.cs``mcu_serial_bridge.dll` 的P/Invoke封装;本仓库源码目录未包含该本机库。
CAN ID、串口参数、IO位和驱动方向属于实车安全边界,未经明确要求不得修改。
+46
View File
@@ -0,0 +1,46 @@
# 项目概览
## 项目定位
`MyParking` 是停车机器人控制软件的当前正式开发版本,主体为 C# 插件工程。项目目标是让多舵轮停车机器人完成底盘运动、车辆状态获取、轨迹跟踪、原地自转和后续停车作业;当前研发重点仍是单车闭环和实车联调,多车协同尚未形成可运行实现。来源:`README.md``MultiWheelC/PilotConfig.cs``Shared/Fleet/FleetKinematics.cs`
项目不是可直接 `dotnet run` 的独立应用:
- `MultiWheelC.dll` 由 Clumsy 宿主加载,提供 C 层动作、控制和 `MovementTest` 入口。
- `MedullaAdapter.dll` 由 Medulla 宿主加载,负责 M 层硬件通信和周期逻辑。
- `CommonUsage.dll` 提供通用底盘模型和四轮几何解算。
对应入口类型为 `MultiWheelC/PilotDefinition.cs::PilotDefinition``MedullaAdapter/DiverCartDefinition.cs::DiverCartDefinition`;仓库中没有 `Program.Main`
## 车辆与业务场景
- 代码接口面向四个可转向轮组,并暴露8个驱动电机的位置/速度反馈和4个舵角反馈。M/C IO定义见 `PilotDefinition``DiverCartDefinition`
- 车体支持正常前后行驶、倒车轨迹、任意固定运动方向β下的滚动运动、蟹行和停车后原地自转。主要入口见 `MultiWheelC/Movements/``MultiWheelC/Experiments/`
- 夹臂速度、位置、限位和报警IO已经接入,但轮胎识别、自动钻车、释放车辆和完整停车作业状态机尚未在当前新版流程中完成。来源:`PilotConfig.cs``PilotDefinition.cs``README.md`
- 多车模型数据类型已经预留,但多车配置位于 `PilotConfig.cs``#if false``FleetKinematics.cs` 只有占位注释,不能把当前代码视为已支持多车编队。
## 整体运行流程
典型新版轨迹跟踪流程:
1. Clumsy 宿主通过 `MovementTest` 或动作计划启动 `TrajectoryTrackingMovement`
2. 动作停车并通过 `PrepareWheelsForward` 将舵轮预对齐到本段运动方向β。
3. `ParkingVehicleStateProviderFactory` 组合 Detour 位姿和轮组反馈速度。
4. `ParkingGeometricController` 完成轨迹投影、横纵向控制、GCP分配和完成条件判断。
5. `GcpCommandExecutor``MultiWheelChassisAdapter` 将SI单位命令转换到当前运动坐标系,再调用 `CommonUsage.Chassis.MultiWheelChassis`
6. M层周期逻辑将底盘和夹臂命令发送到MCU/CAN,并把反馈通过宿主IO返回C层。
详细调用链见 `architecture.md`,接口和坐标语义见 `interfaces.md`
## 当前阶段
- 单车基本运动、新版轨迹跟踪、倒车直线、45°蟹行直线、原地自转和组合动作均有宿主测试入口。
- 新版控制默认采用 Stanley 横向控制和 PID 纵向控制;轨迹实验和控制周期诊断可输出CSV。
- 当前代码能力不等于完整实车验收,特别是高速、曲线倒车、蟹行曲线、长期稳定性和异常工况仍需单独验证。
## 待确认
- Clumsy/Medulla 宿主的正式版本、插件部署目录和启动顺序。
- `PilotDefinition.Conf` 在实车上的持久化文件位置、加载时机和发布流程。
- 真实车辆最终几何参数、舵轮零位/限位和不同车辆配置的权威来源。
- 完整停车业务的产品流程、安全状态机和验收指标。
+124
View File
@@ -0,0 +1,124 @@
# 问题与验证状态
## 已解决(代码层)
### 轨迹数值检查和插值入口重复
- 现象:多个轨迹文件分别实现有限值检查和插值,语义容易漂移。
- 处理:Shared增加 `NumericGuard`;轨迹采样和投影共用 `Trajectory2D.InterpolateSegment()`
- 位置:`Shared/Validation/NumericGuard.cs``Trajectory2D.cs``TrajectoryProjector.cs`
- 验证范围:代码结构已统一;无自动化单元测试记录。
### 交叉/邻近轨迹可能发生投影进度跳变
- 原因:每周期在全轨迹搜索最近线段可能跳到空间上接近但进度不连续的线段。
- 处理:首周期全局搜索,后续按上次弧长使用后退0.10m、前进1.00m窗口,并在距离并列时优先原进度。
- 位置:`TrajectoryProjector``ParkingGeometricController`
### 倒车曲率和反馈符号不完整
- 处理:轨迹使用有符号参考速度,Yaw保持车头方向;横向误差继续相对轨迹执行点序定义,共同转角不再乘行驶方向,航向修正和曲率前馈保留倒车反号。
- 现有入口:`NewControllerReverseStraight4mTest`
- 验证:`MultiWheelC.Tests` 的8个无实车符号场景通过;2026-08-19第六轮实车记录中,倒车零偏置终点误差约15.9mm和12.8mm+150mm偏置横向误差由约-150.4mm收敛到-5.0mm、终点误差约18.1mm,已与正向直线精度接近。
- 限制:直线倒车有测试入口,曲线倒车仍归入待验证。
### 终点零速参考可能提前停车且无法收敛
- 处理:增加终点制动预瞄和单向低速逼近;越过终点不反向修正。
- 完成条件:剩余弧长、终点距离、终点航向和β方向轮组实际速度全部满足。
- 位置:`ParkingGeometricController``PilotConfig.ParkingControl.cs`
### 状态配置和终点速度来源不一致
- 处理:统一通过 `ParkingVehicleStateProviderFactory` 创建状态链;终点速度使用Vx/Vy沿β投影,不再让Detour横向速度单独影响普通前进判停。
- 后续补充:`WheelFeedbackVehicleStateProvider` 已保留并滤波轮组反馈Vy,支持蟹行纵向速度投影。
### 动作开始/结束时舵轮模式不明确
- 处理:轨迹跟踪开始前自动准备β方向;原地自转先准备自转姿态,完成后回正;组合计划在运动前预检全部段。
- 位置:`PrepareWheelsForward``TrajectoryTrackingMovement``MultiWheelRotateInPlace``MotionPlanExecutor`
### Detour持续小阶跃会直接扰动控制轨迹
- 处理:状态层已分离Detour原始坐标与任务控制坐标;疑似阶跃期间使用滤波后的轮组 `Vx/Vy/Vw` 短时预测,有限小偏移经连续帧确认后更新坐标变换,大幅或超时未恢复的变化返回状态不可用。
- 自转保护:动态航向阈值考虑实际轮组Vw和Detour源帧间隔;近似原地自转期间禁止自动吸收坐标偏移;位置单独异常时航向闭环可继续,到位停车后再恢复完整位姿,未恢复时阻止下一运动段。
- 位置:`DetourVehicleStateProvider``WheelFeedbackVehicleStateProvider``MultiWheelRotateInPlace``PilotConfig.ParkingControl.cs`
- 验证:第三轮实车组合动作中发生一次约47mm/3.44°的原始坐标变化,自动连续化计数增加后控制轨迹保持连续,动作最终以约24.7mm剩余距离、2.8mm横向误差完成。
## 待解决
### 完整停车作业流程尚未实现
- 缺少或未接入新版流程:轮胎识别、自动钻车、夹抱/释放动作编排、完整安全状态机。
- 现有 `PilotConfig` 和IO字段不能视为业务流程已经完成。
### 多车能力仍是占位
- `Shared/Fleet/FleetKinematics.cs` 没有实现。
- `PilotConfig.cs` 的多车区域在 `#if false` 中,不参与当前编译。
- 当前没有车队状态、通信、命令分配、安全降级和多车测试闭环。
### Detour跳变判据和动作段衔接仍需收敛
- 2026-08-19第四轮记录包含16次独立原地自转:4次未出现候选、3次候选后恢复、9次因候选超时变为不可用,共25个候选片段。全部由 `PositionInnovationExceeded` 触发,没有 `HeadingInnovationExceeded`;航向创新峰值约7.1°,动态允许量约7.5~9.3°,说明当轮航向动态判据不是主要失败点。
- 候选开始时Detour单帧平移中位数约53mm、最大约162.3mm,轮组 `Vx/Vy` 接近零;Detour新帧间隔约98.2134.4ms,中位数约114.4ms。候选开始时 `l_step` 为283,其中9/25发生在 `l_step<=3`,另有 `l_step=61` 的自转未触发候选,继续证明 `l_step` 不能单独作为有效性门限。
- 同轮7次直线中6次完成,其中2次完成有限坐标连续化;1次在约190mm原始阶跃后安全失败。组合动作完成,仅出现约47.7mm的短候选并自行清除。小范围连续化已有正面证据,大幅阶跃仍应保持安全边界。
- 已实施“自转期间位置/航向分离、停车后再恢复位置”的动作衔接,编译和统一打包通过,但尚待实车验证。预期它解决位置单独异常导致的自转中断,不代表已经消除Detour原始位置阶跃。
- 当前实现先读取Detour观测,再把轮速预测推进到本机当前 `Stopwatch` 时刻并比较两者;Detour观测对应的源时刻通常更早,因此比较时刻尚未严格对齐。0.4m/s且Detour约0.11s一帧时,这种时序差可产生约44mm表观位置创新,是固定40mm阈值可能过严的重要原因。短期可评估有界的“基础余量 + 平面轮速 × 源帧间隔”,但成熟修正应保存短时轮速/里程计历史,将观测与同一源时刻的预测比较,再把校正状态预测到当前时刻;该方案尚未实施。
- 健康 `l_step` 下仅凭预测创新不应轻易自动改写坐标系;在完成同一时刻比较前,不继续通过反复放宽阈值堆叠补丁。
- 不建议通过全局放宽0.60s确认窗口、40mm残差或允许自转期间完整坐标修正来掩盖问题。
- 2026-08-19第六轮记录确认普通正向/倒车直线、曲线和小范围运动中坐标连续化总体可用,主要剩余失败集中在原地自转:有样本在 `l_step=94` 时连续3个Detour新帧航向创新超限,状态层先短时使用轮组Vw预测,第三帧才安全终止,说明单帧航向容错已按设计生效;另有约163mm、207mm的位置不连续或候选不稳定导致停车后恢复失败。
- 当前决定:保持普通运动150mm自动连续化上限、自转最大角速度30°/s和角加速度40°/s²,不以全局放宽阈值或提高自转速度掩盖Detour退化。原地自转后的条件化“仅位置连续化”仍是可评估方案,但尚未实施;持续航向异常需要Detour质量/重定位语义或额外可信航向来源才能进一步收敛。
### 自动化测试和CI覆盖有限
- 现有 `[MovementTest]` 是Clumsy宿主人工/实车入口,不是 `dotnet test`
- `MultiWheelC.Tests` 是不进入正式打包的独立可执行回归项目,现已覆盖Stanley前进/倒车横向符号的8个场景;应长期保留其源码,`bin/obj`仅为可删除编译产物。
- 轨迹数学、坐标变换、状态跳变过滤和终点策略仍缺少标准测试框架下的可重复单元测试,当前也没有CI。
### 运行和发布信息不完整
- 宿主版本、正式插件目录、启动顺序、运行时配置文件位置和发布审批流程待补充。
- `mcu_serial_bridge.dll` 不在当前源码目录,实车运行依赖外部部署。
## 待验证
### Detour静态基线与运动跳变条件
- `Experiments/DetourStaticDiagnosticTest.cs` 已提供只读宿主测试,记录 `x/y/th/tick/l_step`、数据年龄、接口耗时、帧间差、四轮反算 `Vx/Vy/Vw` 及单轮反馈。
- 2026-08-18约881s静态记录中没有复现5~7cm阶跃:Detour新帧最大位移9.21mm、2s前后中值最大持续偏移6.11mm,前后30s位置中值变化约15.5mm;该结果是当前环境的正常静态基线,不证明运动中阶跃已经消失。
- 同次记录确认接口无失败或乱序,Detour实际新帧间隔中位数约109.94ms(约9Hz),数据年龄中位数57.19ms、最大134.49ms`getCartLocation()`调用本身中位耗时仅0.0139ms,说明接口主要返回缓存的最新定位。
- 静止时直接差分Detour位姿仍产生表观速度:线速度中位数约0.0132m/s、P95约0.0320m/s,角速度绝对值P95约0.680°/s;继续使用轮组反馈作为速度闭环来源是合理的。轮组反算在本次静态记录中基本为零,但仍需运动实验验证。
- `l_step` 正常基线主要为2,并约每30s出现一个新帧值3,且未与明显位姿变化相关;精确定义、异常阈值以及Detour是否提供显式重定位/坐标重置状态仍待确认。
- 运动记录进一步表明 `l_step` 与位姿创新并非一一对应。建议仅将其作为分级健康信号:低值稳定可支持恢复判断,高值持续且同时存在异常创新时才支持停车;实际分级阈值仍需结合部署端Detour/MDCS版本确认。
- C层轨迹CSV现已同时记录原始/滤波轮组 `Vx/Vy/Vw`、轮组采样时刻、Detour源帧间隔和预测时刻、实际/允许的位置与航向创新、候选触发原因、估计器状态及不可用原因。该记录解决了字段缺口,但Detour源 `tick` 与本机单调时钟/轮组采样时钟之间尚未建立统一时间轴,仍需通过新实验和Detour `tick` 语义确认后才能定量评价预测误差。
### 控制周期和舵轮响应
- 代码已经记录控制周期分段耗时、请求/限速后GCP命令和四舵角;M层已有轮速/舵角诊断CSV。
- 差速转舵角速度前馈默认增益0.9,曲率预瞄默认0.15s/0.12m。
- 2026-08-19第六轮共9131个轨迹控制周期:周期中位数约31.24ms、P95约32.68ms、最大约59.44ms;控制计算总耗时中位数约0.12ms、P95约0.22ms。当前C层计算不是主要周期瓶颈,历史约110ms现象不应继续归因于控制算法计算量。
- 不同速度、载荷下的舵轮物理响应和前馈参数仍需按具体工况验证。
- CAN/MCU正常运行逻辑风险较高;除诊断外不应在没有明确方案和实车回退措施时修改。
### 尚未覆盖的运动工况
- 曲线倒车。
- 45°/90°蟹行曲线及不同β下的机械限位。
- 非零β曲线中 `MultiWheelChassis.GetCarSpeed(true)` 的坐标一致性:`SetOriginBias` 会旋转 `sw.Position`,需要确认 `ReadAngle()` 返回值在该解算中的参考系与之匹配;当前不能仅由蟹行直线推断曲线速度反馈正确。
- 0~1.2m/s范围内曲率预瞄、速度滤波和GCP角速度限制的参数适用性。
- 高速、低附着、载车后质量/惯量变化、定位丢失和急停恢复。
### 参数与实车配置一致性
- `参考文档/*.json` 是否与当前实车配置一致待确认。
- `PilotDefinition.CarLength/CarWidth`、宿主底盘轮子布局、`ControlPointRadius`、舵轮零偏和机械限位需要按车辆编号核验。
## 排障入口
- C层轨迹CSV`TrackingExperimentRecorder`,默认宿主目录 `TrackingExperiments/`
- C层Detour静态诊断:`DetourStaticDiagnosticTest`,默认宿主目录 `DetourStaticDiagnostics/`
- C层周期耗时:`ParkingGeometricController.LastCycleTiming``*_timing.csv`
- M层轮速/舵角:`StartWheelSpeedDiagnostic()` / `StopWheelSpeedDiagnostic()`,默认 `logs/wheel-speed/`
- 底盘分解失败:`MultiWheelChassisAdapter.LastFailureReason``GcpCommandExecutor.LastFailureReason`
- 状态失败:`DetourVehicleStateProvider.LastFailureReason``WheelFeedbackVehicleStateProvider.LastFailureReason`
+51
View File
@@ -0,0 +1,51 @@
# 当前进展
更新日期:2026-08-19。这里只保存当前状态,不作为完整开发历史。
## 已完成/已接入
- 三项目解决方案与统一构建打包脚本:`CommonUsage``MedullaAdapter``MultiWheelC`
- Shared的SI运动模型、坐标变换、角度工具、数值校验和 `MultiWheelChassisAdapter`
- 弧长参数化 `Trajectory2D`、统一插值、轨迹投影窗口和进度连续性。
- 默认 Stanley 横向 + PID 纵向的新版轨迹控制链,横向控制器可注入替换。
- 前进4m、倒车4m、45°蟹行4m、直线-左半圆-直线和组合运动宿主测试入口。
- Detour位姿与轮组反馈组合状态源:Vx/Vy用于控制速度,Vw用于短时位姿预测和动态航向合理性判断,最终控制Omega仍来自Detour。
- Detour源 `tick/l_step` 诊断、跳变候选确认、有限小偏移任务坐标连续化、自转期间禁止自动吸收偏移,以及超时状态不可用保护。
- 起步释放、曲率前馈预瞄、GCP角速度限制、终点制动预瞄和单向低速收敛。
- 原地自转舵轮准备、航向PID、超时保护和完成后回正;自转期间位置/航向有效性已分离,到位停车后才恢复完整位姿并决定是否释放下一段。
- Detour单帧航向异常已增加连续帧/短时预测确认;第六轮实车数据确认前两帧异常由轮组Vw预测承接,持续到第三个异常新帧时才安全终止。
- C层轨迹/周期CSV与M层轮速/舵角诊断记录。
- C层Detour静态诊断入口,记录源时间、`l_step`、位姿帧差和轮组静态反馈;已有约14分41秒实车静态基线。
- C层轨迹CSV已补充原始/滤波轮组 `Vx/Vy/Vw`、轮组与预测时刻、Detour帧间隔、实际/允许创新、候选原因、估计器状态和不可用原因。
- 停车控制参数集中到 `Configuration/PilotConfig.ParkingControl.cs`
- `MultiWheelC.Tests` 已提供不依赖实车的Stanley前进/倒车横向符号回归,8个场景通过;该项目不进入正式解决方案和打包脚本。
- 本次建立工作区/项目AGENTS导航和 `docs/` 按需知识库。
以上表示代码入口存在,不表示全部实车工况已经验收。
## 当前进行方向
- 普通正向/倒车直线、曲线和小范围运动中坐标连续化已基本可用;当前单车状态估计的主要未决问题收敛到原地自转时Detour偶发持续退化。
- 暂时冻结普通运动跳变阈值和30°/s、40°/s²自转参数,等待Detour接口语义后再决定是否实施自转结束后的条件化仅位置连续化或调整航向恢复策略。
- Detour对接最小问题已整理到 `docs/detour-information-checklist.md`,不要求取得源码。
- 验证非零β运动系,当前已有45°蟹行直线入口;曲线蟹行仍需设计实验。
- 在单车和停车流程稳定后研究多车共同搬运的刚体模型与成员命令分配。
## 阻塞/待确认
- Clumsy/Medulla正式宿主、插件部署和配置持久化说明未纳入仓库。
- 完整停车业务流程和验收指标未确认。
- 多车通信协议、车队参考点、车辆布局来源和故障降级策略未确认。
- 最新实车实验数据对控制周期与舵轮滞后的结论尚未沉淀为可复核结果。
- Detour `l_step` 精确定义、显式重定位/坐标重置状态和部署端MDCS恢复策略待确认;当前不能把 `l_step<4` 当作唯一有效性条件。
- Detour `getCartLocation()` 的位姿坐标系、`tick` 采样/解算/发布语义、是否已有独立连续里程计或定位状态接口,以及部署版本和实际里程计/SLAM配置待确认。
- Detour原地自转时 `l_step` 大幅上升、100~200mm级位置不连续和连续航向创新异常的内部原因待Detour负责人说明;仅凭当前位姿接口无法保证持续航向异常时仍可靠完成绝对角度闭环。
- Detour源 `tick` 与本机单调时钟、轮组采样时刻尚无统一时间轴;当前创新比较可能包含数据年龄造成的表观误差。
## 建议下一步
1.`docs/detour-information-checklist.md` 确认 `getCartLocation()` 字段、`tick``l_step`、定位延迟、重定位行为和质量状态接口。
2. 在Detour信息返回前不继续全局放宽状态估计边界,也不提高自转角速度/角加速度;现有偶发定位不可用保持安全停车。
3. 若工程上必须先降低位置型自转失败率,再单独设计仅在纯自转结束、轮组无平移且航向有效时启用的位置连续化;不得扩展到普通轨迹运动,也不得吸收航向偏移。
4. 为轨迹插值/投影、坐标变换、状态跳变候选和终点策略继续补充不依赖宿主的数学回归测试。
5. 单车稳定后,再设计 `FleetMotionCommand → 每车Twist2D` 的车队运动学,不直接启用 `#if false` 旧多车代码。
-34
View File
@@ -1,34 +0,0 @@
单个停车机器人轨迹测试方案
### 测试对象
单台停车机器人(+50kg负载)
### 测试曲线
1. 直线:前进/后退2m、速度0.3m/s、起点终点静止
2. 转弯:左转/右转组合前进/后退、转弯半径1m、曲率1.0、速度0.3m/s
3. 原地自转:±90°/±180°、角速度10°/s、20°/s、起点终点静止
### 评价指标
横向误差 RMSE、最大横向误差;航向误差 RMSE、最大航向误差;速度误差 RMSE、最大速度偏差;
角速度或转角指令的变化曲线;最终位置误差、最终航向误差
### 展示形式
理想轨迹与实际轨迹对比图、横向/航向误差随时间变化图、参考速度与实际速度对比图、角速度指令曲线
### LQR调参策略
归一化状态/控制量:一般Q、R初始选择对应控制量最大值的平方的倒数
或者使用Bryson’s Rule来给 Q、R 一个很好的初始猜测
贝叶斯优化在仿真中自动调节Q、R参数
ALQR:在线估计最新参数实时重新求解
----------------------------------------
不调参:学习式
### 杂项
1. M层获取实际小车的速度信息与位置信息并保存、使用python可视化来量化跟踪误差
2. 自行车模型改动、过于局限于阿克曼小车的运动学限制
先用 Bryson’s Rule + 贝叶斯优化在仿真里把 Q/R 调到一个不错的基准。
上实车时采用自适应 LQR:在线估计关键参数(尤其是轮胎刚度),实时更新 K。
C# 实现的话:
矩阵运算继续用 Math.NET
贝叶斯优化可以调 Python 库,或者自己写简单版本
在线参数估计(RLS)用 C# 写很轻松
-47
View File
@@ -1,47 +0,0 @@
编译命令:
powershell -NoProfile -ExecutionPolicy Bypass -File .\build-and-package.ps1
1. TrajectoryPoint.cs 已完成
2. Trajectory2D.cs 下一步
3. TrajectoryProjection.cs 定义一次投影结果
4. TrajectoryProjector.cs 实现连续线段投影
5. TrajectoryBuilder.cs 原始离散点转标准轨迹
1. VehicleState.cs
2. FirstOrderLowPassFilter.cs
3. VelocityEstimator2D.cs
4. IVehicleStateProvider.cs
5. DetourVehicleStateProvider.cs
private const double LinearVelocityFilterTimeConstantSeconds =
0.15;
private const double AngularVelocityFilterTimeConstantSeconds =
0.20;
PathTrackingContext.cs
LateralControlCommand.cs
ILateralController.cs
ILongitudinalController.cs
GcpMotionCommand.cs
AckermannGcpAllocator.cs
StanleyLateralController.cs
PidLongitudinalController.cs
GcpCommandExecutor.cs
ParkingGeometricController.cs
把前后GCP转角分解成两个模态:
共同转角 = (前GCP转角 + 后GCP转角) / 2
差动转角 = (前GCP转角 - 后GCP转角) / 2