# 多车联动(MultiVehicleSync)算法原理 本文说明 **MDCSToolBox** 中 N 车联动的架构与几何原理。Tutorial `MultiWheelC` 与产品 `StandardMultiWheelLifterC` 均继承 `MultiWheelPilotDefinition`,调用 `InitMultiVehicleCoordination()` 启用同步。 ## 1. 总体架构 每台 AGV 运行 **Detour + Medulla (M) + Clumsy (C)**: | 实例 | Detour `shareObjectTag` | Medulla/Clumsy `soTag` | Clumsy 角色 | `MultiVehicleMasterEndpoint` | Clumsy `port` | Detour HTTP | `CarNum` | |------|-------------------------|------------------------|-------------|------------------------------|---------------|-------------|----------| | AGV1 | `Multi1` | `Multi1` | 主车 master | `/` | 8008 | 4321 | 1 | | AGV2 | `Multi2` | `Multi2` | 从车 slave | `127.0.0.1:8008` | 8009 | 4421 | 2 | Medulla web API(与 Clumsy 车队同步端口无关):AGV1 **8007**,AGV2 **8108**(单机部署时 Medulla #2 不可占用 8008,该端口留给 Clumsy 主车)。 **同机隔离(Core 层)** - Detour 写入 `DObject("DetourPos{shareObjectTag}")`;Clumsy 读取 `DObject("DetourPos{soTag}")`,tag 必须一致。 - Clumsy 通过 `detourHost`/`detourPort`(`clumsyconsole.json` 或 `clumsy.json`)访问对应 Detour HTTP 端口(4321/4421)。 - 每台 Clumsy 绑定独立 `WebAPI.port`(8008/8009),register/notify 使用 `VehicleSyncInfo.Port` 寻址。 M 与 C 通过 `CartActivator` 共享 DObject(`MedullaCartUpperIO{tag}`),不直接 DLL 调用。 ```text 从车 --GET /multi-vehicle-register--> 主车:8008 (上报本车位姿、layout、Port) 主车 --GET /multi-vehicle-notify----> 从车:8009 (广播车队中心、fleet 命令、完整 fleet 状态) ``` ## 2. 数据模型 定义于 `MDCSToolBox/Clumsy/Pilot/MultiWheel/VehicleSyncModels.cs`。 ### 2.1 VehicleSyncInfo(从车 → 主车) | 字段 | 含义 | |------|------| | `Master` | 是否主车 | | `Ip` / `Port` | 本车同步端点(notify 寻址) | | `PosAvailable` | 本车 Detour/SLAM 是否有效 | | `X/Y/Th` | 本车世界位姿(mm, deg) | | `LayoutX/Y/Th` | 本车在车队坐标系下的 **固定 slot** | | `Aligned` | 本车互识别/精度是否满足(见 §4) | ### 2.2 VehicleSyncNotification(主车 → 从车) 除原 fleet 命令外,主车广播 `CenterX/Y/Th`(世界系车队中心)、`SyncTh/SyncDistance`、完整 `Fleet` 快照等。 ## 3. 编队几何与车队中心 ### 3.1 编队布局(双车) 布局由 `TestCarSyncDistance` 与 `TestCarSyncTh` 唯一确定(不再有独立 slot 表):每车相对车队中心沿编队方向对称分布于 ±`TestCarSyncDistance/2`,从车朝向额外翻转 180°。实现见 `GetLayoutPose`。`TestCarSyncDistance` 同时是检测环(`detectDistance = TestCarSyncDistance - DeltaDetectCenter`)与 SLAM 位姿环的唯一间距来源,二者据此自洽。 ### 3.2 车队中心反推(非位姿平均) 世界系中心 **不是** 对各车 X/Y/Th 取平均,而是由 **当前 `PosAvailable==true` 的车** 按固定 slot 反推: ```text Transform2D(fleetCenter, layoutSlot) = carWorldPose ⇒ fleetCenter = Transform2D(carWorld, -layoutSlot) // 实现见 InferFleetCenterFromCar ``` - 多台有效车:对各车反推的中心取平均(理论上应一致,差异可作互检)。 - 零台有效车:无世界系中心 → **手动模式**仍可通过互识别保持编队;**自动模式**门控拒绝(见 §5)。 ## 4. 互识别接口(插件实现) 基类 `MultiWheelPilotDefinition` 提供虚方法: ```csharp protected virtual (bool Valid, float Dx, float Dy, float Dth) DetectNeighborBias(float detectDistance); ``` - 基类:`MultiVehicleUseDetect` 门控 → 计算 `Aligned` → `SendMotion(localCompensate*)`。 - **具体检测**(雷达名、两腿/反光板等)由车体插件重写。 - 产品:`StandardMultiWheelLifterC` → `VehicleMoverHelper.GetSyncBias` - Tutorial:默认 `Valid=false`(无检测硬件时可纯靠手动+SLAM) ## 5. 手动 / 自动门控 | 模式 | 条件 | |------|------| | 手动 | 始终可用;可无 Detour,靠 `DetectNeighborBias` 纠正 | | 自动 | 主车要求 fleet 中 **至少 1 台** `PosAvailable`;否则 toast 拒绝并清零 `MultiVehicleAutoEnabled` | ## 6. 主循环(默认 50ms) 逻辑位于 `MultiWheelPilotDefinition.MultiVehicle.cs` 的 `TickMultiVehicle`: 1. 判断 master/slave、手动/自动是否启用 2. 读取本车 SLAM(若 `PosAvailable`) 3. 主车:计算 fleet 命令;从 notification 同步 4. `GetLayoutPose` → 本车布局位姿(由 `TestCarSyncDistance`/`TestCarSyncTh` 推导) 5. `DetectNeighborBias`(可选)→ 互识别补偿 6. 有有效中心时 SLAM `posBias` 补偿 7. `SendMotion(fleetVx, FrontTh, RearTh, localCompensate*)` 8. master:register 汇总 + notify 各从车(按 `Ip:Port`,跳过自身 `CarNum`) ## 7. 配置文件 见 [RunAndDeploy.md](./RunAndDeploy.md):`clumsyconsole.json`(soTag/port/detour)、`clumsy.json`(msConf slot/主车端点)、`detour.json`(shareObjectTag/DetourPort)。 修复与迁移记录见 [BugFixes.md](./BugFixes.md)。