Files
Tutorial/docs/MultiVehicleAutoSyncReview.md
T
ruifeng.zhouandCursor d935e847ec Add MultiWheelS Simple scheduling plugin for fleet sync
Introduce the MultiWheelS (SimpleComposer host) plugin with the
MultiVehicleCar definition so fleet auto-sync is driven via Simple
dispatch instead of the hand-rolled AGV.FleetGo path. Simplify
MultiWheelC.AGV to BasicInterface, drop the obsolete FleetGo helper
and FleetStraightMovementTest, wire MultiWheelS into Tutorial.sln,
and update RunAndDeploy docs plus add the MultiVehicleAutoSyncReview
notes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 23:12:23 +08:00

197 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 自动多车联动机制问题分析(MultiVehicleAutoSyncReview
本文档记录 Tutorial `MultiWheelC` 中**自动运行多车联动**机制的潜在 bug 与工程化缺陷。
**本次仅作分析归档,不包含代码修复。**
相关实现:
- [PilotDefinition.cs](../MultiWheel/MultiWheelC/PilotDefinition.cs) — 50ms `MultiVehicleLoop` / `TickMultiVehicle`
- [ChassisController.cs](../MultiWheel/MultiWheelC/ChassisController.cs) — 路径跟踪 → `MultiVehicleAuto*` 回调
- [VehicleSyncModels.cs](../MultiWheel/MultiWheelC/VehicleSyncModels.cs) — register/notify 数据模型
- 算法背景见 [MultiVehicleSync.md](./MultiVehicleSync.md)
---
## 自动联动数据流
```text
Simple 调度 MultiVehicleCar (MultiVehicleSync=true)
→ 编译 agv.BasicGo(..., multiVehicleSync:true)
→ BasicInterface 设置 _controller.MultiVehicleSync = true
→ MultiWheelGeometricController.Track() 跟踪路径
→ MultiVehicleSendMotion(speed, frontTh, rearTh, idealPos, idealAngle)
→ PilotDefinition.MultiVehicleAutoVx/FrontTh/RearTh + AutoEnabled=true
→ TickMultiVehicle (主车) 广播 notify → 从车跟 FleetVx/FrontTh/RearTh
→ 各车 SetOriginBias(layout) + SendMotion + 检测/SLAM 补偿
```
---
## 问题清单
| ID | 严重度 | 问题摘要 | 定位 |
|----|--------|----------|------|
| A | 高 | 在会被重新赋值的 `MultiVehicleFleet` 字段上 `lock`,互斥失效 | PilotDefinition.cs ~185-205, 多处 lock |
| B | 高 | 路径结束/回调早退时自动速度不清零,车队滑行 | PilotDefinition.cs 44-47, ChassisController.cs 46-57 |
| C | 高 | 主车 fleet 表无 TTL,从车崩溃后仍认为编队就绪 | PilotDefinition.cs ~178-186, 505-508 |
| D | 中 | 曲线路径广播单一 FrontTh/RearTh,丢弃 idealPos/idealAngle | PilotDefinition.cs 417-419,616; ChassisController.cs 46 |
| E | 低 | ControlPointRadius 与 SendMotion localControlRadius 硬编码冲突 | PilotDefinition.cs 519, 616 |
| F | 中 | notify 经 GET query 串 fire-and-forget,无背压/限频 | PilotDefinition.cs 846-854, 696-702 |
| G | 中 | 车队中心 CenterX/Y/Th 跨线程非原子读写 | PilotDefinition.cs 348,434-436; ChassisController.cs 60-66 |
| H | 中 | 关闭互识别时 SLAM 全程丢失不停车 | PilotDefinition.cs 485, 535 |
---
## A. lock 目标字段会被整体替换
**描述**
`MultiVehicleFleet` 在 notify 处理中被整体重新赋值:
```csharp
lock (MultiVehicleFleet)
MultiVehicleFleet = notification.Fleet ?? new Dictionary<int, VehicleSyncInfo>();
```
同时其它线程(register、Tick、VisualizeFleet)也在 `lock (MultiVehicleFleet)` 上同步。
`lock` 的对象是**引用本身**;字段被赋新字典后,不同线程可能分别锁旧引用与新引用,**互斥语义失效**,存在 torn read / 丢更新风险。
**影响**
高并发下 fleet 快照不一致、Aligned/fleetReady 判定抖动,极端情况下补偿或 notify 基于过期 fleet 状态运行。
**建议修复**
- 使用 `private readonly object _multiVehicleFleetLock = new();` 作为唯一锁对象;或
- 永不替换字典引用,改为 `Clear()` + 逐条 `Update`;或
- 使用 `ConcurrentDictionary` + 不可变快照广播。
---
## B. 自动速度残留滑行
**描述**
`MultiVehicleAutoVx / FrontTh / RearTh`PilotDefinition 第 45-47 行)由 `ChassisController.MultiVehicleSendMotion` 写入。
当路径跟踪结束、DriveTask 完成,或回调因 `MultiVehicleFleet.Count != FleetNum` 提前 return 时,**这些字段不会被清零**。
`MultiVehicleAutoEnabled``[AsUpperIO(timeOutReset = true)]`,在 IO 超时前主车仍认为自动模式开启,继续读取**最后一次非零速度**并广播给从车。
**影响**
路径到达终点后车队仍按末速度滑行;与手动模式「松杆即停」行为不一致,存在 overshoot / 碰撞风险。
**建议修复**
- `DriveTask` 完成或 `_controller` 置 null 时显式清零 `MultiVehicleAuto*``MultiVehicleAutoEnabled=false`
- 主车广播前检查速度命令新鲜度(时间戳 / 控制器活跃标志);
- 从车侧对 `FleetVx` 增加与 notify 相同的 freshness 超时(目前已有 notification 超时,但速度字段本身无独立 TTL)。
---
## C. 主车 fleet 成员无 TTL
**描述**
从车通过 `/multi-vehicle-register` 上报,主车仅 `MultiVehicleFleet[CarNum] = info` 覆盖写入,**从不删除过期条目**。
`fleetReady = (fleetCount == MultiVehicleFleetNum)`(第 505-508 行)只检查数量,不检查从车是否仍在线。
**影响**
从车进程崩溃或网络断开后,主车仍认为编队完整并继续 `SendMotion` + notify,存活车辆按过期 layout/DetectOk 运行,编队几何错误或未检测到邻车仍运动。
**建议修复**
- `VehicleSyncInfo` 增加 `LastSeenUtc`register/notify 收到时刷新;
- 主车 Tick 剔除超过 N×SyncInterval 未更新的成员;
- `fleetReady` 要求所有成员 `LastSeen` 新鲜。
---
## D. 曲线路径几何与 idealPos 丢弃
**描述**
主车从 `MultiVehicleAutoFrontTh/RearTh` 广播单一 `(FrontTh, RearTh)`,各从车原样套用(第 417-419、616 行)。
这对**平移、蟹行、绕共有旋转中心**近似成立,但对**一般曲线路径**,各车相对瞬时曲率中心位置不同,应使用不同舵角。
`MultiWheelGeometricController` 已计算 `idealPos / idealAngle` 并传入 `MultiVehicleSendMotion`,但 [ChassisController.cs](../MultiWheel/MultiWheelC/ChassisController.cs) 回调签名中**完全忽略**这两个参数,只转发 speed + frontTh + rearTh。
**影响**
自动模式下走弧线/贝塞尔时,从车仅靠检测/SLAM 补偿「事后纠偏」,存在滞后与稳态横向误差(与原地旋转 PI 纠偏同类问题,但自动模式无专用 PI)。
**建议修复**
- notify 增加 `FleetIdealX/Y/Th` 或 per-car layout 目标位姿;
- 从车按 layout 目标做前馈 + 补偿,而非共用 frontTh/rearTh
- 或限制自动联动仅支持直线段(文档/配置门控)。
---
## E. 控制半径硬编码冲突
**描述**
`chassis.SetOriginBias` 后设置 `ControlPointRadius = syncDistance / 2f`(第 519 行),但 `SendMotion` 传入 `localControlRadius: 510`(第 616 行)硬编码,覆盖几何含义。
**影响**
编队间距配置变更时,实际控制点半径仍固定 510mm,补偿/转向几何与 `TestCarSyncDistance` 不一致,调参困难。
**建议修复**
统一使用 `syncDistance / 2f` 或配置项 `MultiVehicleControlRadius`,删除 magic number 510。
---
## F. notify 传输层缺陷
**描述**
主车每 50ms 对每台从车 `FireAndForgetNotify`:将完整 `VehicleSyncNotification`(含整队 `Fleet` 字典)JSON 序列化后嵌入 **GET query 字符串**(第 846-854 行),共享单个 `HttpClient`,无 await、无重试、无背压。
**影响**
- 车队规模增大时 URL 超长(浏览器/服务器 query 上限);
- 从车处理慢时请求堆积;
- 乱序到达时从车可能短暂应用旧 notification
- GET 语义不适合大 payload,且不利于缓存/安全审计。
**建议修复**
- 改为 POST + JSON body
- 仅广播命令增量 + 各车 register 拉取全量;
- 限频 / 合并同 tick 多次 notify
- 记录 notify 序列号供从车丢弃旧包。
---
## G. 车队中心跨线程 torn read
**描述**
`MultiVehicleLoop`(后台线程)写入 `CenterX/Y/Th`(第 434-436 行)。
路径控制器线程通过 `MultiVehicleGetFleetPos` 读取 `PilotDefinition.Self.CenterX/Y/Th`ChassisController 第 60-66 行)。
三个 `float` 字段无锁、无 `volatile`,可能读到「x 已更新、y/th 仍为旧值」的组合。
**影响**
单帧路径跟踪使用错误车队中心,产生短暂错误 ideal 点 → 自动速度抖动。
**建议修复**
-`struct FleetCenter { float X,Y,Th; long Tick; }` 整体赋值/读取(或 lock 快照);
- 控制器只读上一完整快照。
---
## H. 关闭互识别时 SLAM 丢失不停车
**描述**
`canMove = !MultiVehicleUseDetect || (ownDetectOk && othersDetectOk)`(第 485 行)。
`MultiVehicleUseDetect=false``canMove` **恒为 true**,即使 Detour 丢失、`TryInferFleetCenter` 失败,仍执行 `SendMotion`(仅 pos 补偿分支跳过)。
**影响**
纯 SLAM 自动模式下定位全丢后,车队仍按最后 broadcast 速度运动,无 detect 安全网。
**建议修复**
- 自动模式额外要求 `posAvailable && TryInferFleetCenter` 或 notify `PosAvailable`
- 与手动模式区分:手动可仅靠 detect,自动必须有有效 fleet 中心。
---
## 优先级建议(若后续修复)
1. **P0**:A(锁)、B(停车)、C(TTL)— 直接关系安全停车与状态一致
2. **P1**:G(中心快照)、H(SLAM 门控)、F(notify 传输)— 稳定性与可扩展
3. **P2**:D(曲线几何)、E(半径统一)— 精度与可维护性
---
## 修订记录
| 日期 | 说明 |
|------|------|
| 2026-06-28 | 初版:基于 Tutorial MultiWheelC 自动联动实现与联调日志分析整理 |