车体抽象原理

来自MDCS wiki2
跳到导航 跳到搜索


概述 / 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

  1. SimpleComposer 把业务任务 编译为 TopazScript(路径分段 + 可执行动作)。
    SimpleComposer compiles a business job into TopazScript (path segments + executable actions).
  2. `Car.actualSendScript(script)` 被调度调用。
    The scheduler calls `Car.actualSendScript(script)`.
  3. 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.
  4. 自评估车分支: 调度本地运行 `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.
  5. 任意时刻车体心跳上报 `(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.

相关页面 / See also