车体抽象原理
概述 / Overview
MDCS 把"一台 AGV"抽象为三个层次的协作:Medulla(硬件层 / IO 平台),Clumsy(车体行为层 / 自动驾驶),Simple(车队层 / 调度与交管)。每层各自定义"车"的一个侧面,通过明确的数据契约串联。理解这套分层是开发 MDCS 插件、写适配代码与调试问题的前提。
MDCS abstracts "an AGV" through three cooperating layers: Medulla (hardware / IO), Clumsy (vehicle behaviour / autopilot), Simple (fleet / scheduling). Each layer defines one facet of the vehicle and they connect through well-defined data contracts. Understanding this stack is foundational for any plugin / adaptation work.
三层结构 / Three-layer view
┌─────────────────────────────────────────────────────────────┐
│ Simple (Fleet plane) │
│ • SimpleComposer.RCS.Car / ClumsyCar / 自评估车 subclass │
│ • SimpleCore — DPS 交管 / 寻路 / 包络 / 可达性 │
│ • TopazScript 任务脚本 │
└────────────────┬────────────────────────────────────────────┘
│ 任务 / TopazScript / mission script
│ 上报 / Status (x,y,th,siteId,batt,...)
┌────────────────▼────────────────────────────────────────────┐
│ Clumsy (Vehicle behaviour plane) │
│ • SimpleAgvInterface.Queue(...) │
│ • MovementDefinition: SteeringLineFollowing, │
│ AutoFetchGood, AutoShelfFetching, ... │
│ • DriveTask.WaitDriveTask(IEnumerable<bool>) │
│ • PilotDefinition<T> 自动驾驶宿主 │
└────────────────┬────────────────────────────────────────────┘
│ UpperIO 意图(vCmd, steerCmd, forkTgt, ...)
│ LowerIO 反馈(vEst, sensors, e-stop, ...)
┌────────────────▼────────────────────────────────────────────┐
│ Medulla (Hardware plane) │
│ • CartDefinition + [AsUpperIO]/[AsLowerIO]/[AsInitParam] │
│ • LadderLogic 周期性控制循环 (~50 ms) │
│ • IOObject 体系(Lidar, Camera, Cart 都是) │
│ • DObject 跨进程共享内存 │
└─────────────────────────────────────────────────────────────┘
各层的责任 / Layer responsibilities
Medulla — 硬件层 / Hardware plane
- 抽象具体硬件(CAN / Modbus / S7 / RS485 / USB)成 IO 字段。 - 同步把上位指令写到硬件,把硬件状态读到字段。 - 不做"决策",不知道"任务"。 - 关键文件:`D:\src\M2\OfficialPlugins\CartActivator\CartDefinition.cs`、`Attrs.cs`。 - 关键属性:`[AsUpperIO]`(命令向下流)、`[AsLowerIO]`(状态向上流)、`[AsInitParam]`(启动配置)、`[UseLadderLogic]`(周期循环)。
- Abstracts hardware (CAN / Modbus / S7 / RS485 / USB) into IO fields. - Writes commands down, reads state up, on a fixed tick. - Makes no "decisions" and is unaware of tasks. - Source: `D:\src\M2\OfficialPlugins\CartActivator\CartDefinition.cs` / `Attrs.cs`.
Clumsy — 车体行为层 / Vehicle behaviour plane
- 把"动作"(直行、原地转、自动取货、绕障)封装为 `MovementDefinition` 子类。 - 把 Movement 组合成"业务动作"如 `Fetch / Put / ExitSite`,挂在 `SimpleAgvInterface` 上。 - 不知道车队全局,只看到本车的位置、速度、IO。 - 任务通过 `SimpleAgvInterface.Queue(...)` 流水线执行。 - 关键文件:`D:\src\Clumsy\ClumsyCore\Pilot\MovementDefinition.cs:41`、`D:\src\Clumsy\ClumsyCore\Pilot\PilotDefinition.cs:91-122`、`D:\src\Clumsy\ClumsyCore\Interfaces\SimpleAgvInterface.cs`。
- Packages a motion primitive as a `MovementDefinition` subclass with `IEnumerable<bool> Get()` ticked at `DriveTaskInterval` ms. - Composes business actions (`Fetch / Put / ExitSite`) on top of `SimpleAgvInterface`. - Sees only this vehicle's state.
Simple — 车队层 / Fleet plane
- SimpleCore 提供调度内核:DPS 交管、寻路、包络、可达性、流场。 - SimpleComposer 提供 UI 壳与车型 / 业务插件接口。 - 给单车下发的是 TopazScript(高层意图脚本),由 ClumsyCar 转给车载 Clumsy,或由自评估车在本地翻译为 RFID / Modbus。 - 关键文件:`D:\src\Simple\SimpleComposer\RCS\Car.cs:33` / `ClumsyCar.cs:34`、`D:\src\Simple\SimpleCore\PropType\AbstractCar.cs:346`、`BasicProps\AGVInterface.cs`。
- SimpleCore: scheduling kernel (DPS traffic, pathfinding, envelopes, reachability, flow-field). - SimpleComposer: UI shell + plugin host for car types and business scripts. - Dispatches TopazScript mission scripts; ClumsyCar HTTP-posts to the on-board Clumsy adapter, while self-evaluating cars run `SelfEvaluating(agv, script)` locally and translate to vendor commands.
上下位 IO 数据流 / Upper / Lower IO data flow
UpperIO(命令下行)/ Upper IO (commands going down)
上位 = 调度 / Clumsy → 车体。典型上位字段:
- `vCmd`, `steerCmd` — 速度 / 转向
- `forkHeightTgt`, `forkTiltTgt` — 叉齿
- `brakeOn`, `hornOn`, `ledColor`
- `jackTarget` — 顶升机构
LowerIO(状态上行)/ Lower IO (status going up)
下位 = 车体 → Clumsy / 调度。典型下位字段:
- `vEst`, `steerEst` — 实测速度 / 转向
- `forkHeight`, `forkAtTarget`
- `batteryPercent`, `eStop`, `loadKg`
- 各类传感器(接近开关、负载、温度等)
为什么要分上下位 / Why split upper vs lower
MDCS 强制 命令 / 状态解耦,使得:
- Clumsy 可以在上位写完命令后 立即向上汇报已下发,不必等硬件确认;
- Medulla 的硬件层故障(如 PLC 通讯断)不会污染上层任务状态;
- 测试时可把整车换成模拟器,只要 IO 字段一致,上层代码无感知。
The split enforces command/state decoupling: Clumsy reports "issued" immediately without waiting for hardware ack; Medulla failures don't poison higher-layer mission state; in simulation a vehicle can be swapped for a simulator as long as the IO fields match — the upper stack is unaware.
任务下发完整链路 / Full mission-dispatch path
- SimpleComposer 把业务任务 编译为 TopazScript(路径分段 + 可执行动作)。
SimpleComposer compiles a business job into TopazScript (path segments + executable actions). - `Car.actualSendScript(script)` 被调度调用。
The scheduler calls `Car.actualSendScript(script)`. - ClumsyCar 分支: HTTP POST 到车载 Clumsy;车载 `SelfEvaluating(agv, script)` 解释脚本 → 调 `AGV.Fetch / Put / Go` → `Queue(...)` → `Movement.Get()` → `DriveTask` → Medulla UpperIO。
ClumsyCar branch: HTTP-POST to the on-board adapter; on-board `SelfEvaluating` interprets the script, calls AGV methods, queues Movements, drives Medulla IO. - 自评估车分支: 调度本地运行 `SelfEvaluating(agv, script)`;nested `AGV : AGVInterface` 把 `agv.Go(...)` 翻译为 RFID / Modbus 命令发给车体 PLC。
Self-eval branch: scheduler runs `SelfEvaluating` locally; the nested `AGV : AGVInterface` translates calls to vendor commands. - 任意时刻车体心跳上报 `(x, y, th, siteId, ...)`;调度据此更新地图、推进任务。
Heartbeat from the vehicle continuously reports pose + state; the scheduler updates and advances.
实施建议 / Practical guidance
- 先 Medulla: 没硬件,上层一切免谈。先让 IO 表跑通(手动 set `vCmd=200` 看车动)。
- 再 Clumsy: IO 跑通后写 1 个 `SimpleAgvInterface` 子类、跑直线巡线。
- 最后 Simple: Clumsy 单车可控之后接入 SimpleComposer,做调度。
- 测试每层: Medulla 可以独立测;Clumsy 可以用 IO 模拟器(脚本里直接 set / get 字段);SimpleCore 有单机调度仿真。
- Order: Medulla → Clumsy → Simple. Don't write Simple-side code if Medulla isn't proven yet.
- Test each layer in isolation: Medulla via manual IO sets, Clumsy with an IO simulator, SimpleCore in standalone simulation.