Skip to content

RocketBody Composite Container

> Status: NEW · 已对齐 PCR Master Blueprint v1.0 > 范畴: simulation/state/RocketBody.h > 依赖: dynamics_core/state/{SpatialState, InertialState, AuxState}, plant/hardware/Mech, plant/model/*Spec, avionics/devices/*State, bus/BusBuffer, fcc/FccCore, contracts/*Id > 关键判据: Blueprint §2.6.1 / §7.5


1. 问题陈述

RocketBody跨域复合体——它同时持有:

  • 物理状态(dynamics_core 关心):位姿、速度、质量、惯量、辅助比力/角速度
  • 设备实体(plant + avionics 共同关心):发动机、伺服、栅格舵、IMU、GPS、ICU
  • 航电基础设施(bus + fcc 关心):局部线束缓冲、可选的 FCC 核心
  • 拓扑标记(dynamics_core algebra 关心):当前 WorldStage

没有任何一个子域可以独占它:

  • dynamics_core/ 不能持有它(会破坏 universal 承诺,引入 plant/avionics 依赖)
  • plant/ 不能持有它(会引入 avionics + bus + fcc 依赖)
  • avionics/ 不能持有它(同上,反向)
  • fcc/ 不能持有它(HIL 部署时 FCC 在另一台机器,不能依赖整体)

simulation/state/ 是唯一合法归属(Blueprint §2.6.1 / §7.5 / §7.16)。


2. 类型布局

cpp
// simulation/state/RocketBody.h
namespace sim {

// 物理状态原语来自 dynamics_core(普适,无业务依赖)
using dynamics::SpatialState;
using dynamics::InertialState;
using dynamics::AuxState;

// ─── 设备实体:device-as-unified-entity 形态 ───
// 每个设备一个 struct,自身同时持有 mech(物理面)+ electronic state(电子面)

struct Engine {
    contracts::EngineId             id;
    plant::model::InstallParams     install;
    const plant::model::EngineSpec* spec;     // 非拥有指针,指向 WorldEnv.engine_specs[type_id]

    plant::hardware::EngineMech     mech;     // 物理机械面(燃烧室压力、阀门开度、FSM 阶段)
    avionics::device::ecu::EcuState ecu;      // 电子控制面(Bus 命令解析、FSM 推进、计时器)
};

struct Servo {
    contracts::ServoId              id;
    plant::model::InstallParams     install;
    const plant::model::ServoSpec*  spec;

    plant::hardware::ServoMech      mech;     // 摆角、速率、力矩
    avionics::device::scu::ScuState scu;      // PWM 解析、限位、健康
};

struct Fin {
    contracts::FinId                id;
    plant::model::InstallParams     install;
    const plant::model::FinSpec*    spec;

    plant::hardware::FinMech        mech;     // 偏角、铰链力矩、扰动状态
    avionics::device::fin_ctrl::FinCtrlState ctrl;
};

struct ImuUnit {
    contracts::ImuId                id;
    plant::model::InstallParams     install;
    const plant::model::ImuSpec*    spec;

    avionics::device::imu::ImuState state;    // 只有电子面:噪声状态机、累积量化
    // 物理输入:body.aux.spec_force_b / body.aux.omega_b (tick 时显式传入)
};

struct GpsUnit {
    contracts::GpsId                id;
    plant::model::InstallParams     install;
    const plant::model::GpsSpec*    spec;

    avionics::device::gps::GpsState state;    // 只有电子面
    // 物理输入:body.spatial.pos_ecf
};

struct Icu {
    contracts::IcuId                id;
    const plant::model::IcuSpec*    spec;

    avionics::device::icu::IcuState state;    // 纯电子,无物理输入
};

// ─── 复合容器 ───

struct RocketBody {
    contracts::BodyId             body_id;
    dynamics::algebra::WorldStage world_stage;    // 拓扑标记(详见 §6)

    // ── 物理状态(dynamics_core 关心) ──
    SpatialState   spatial;       // pos_lic, vel_lic, q_lic_body
    InertialState  inertial;      // mass, centroid, inertia
    AuxState       aux;           // spec_force_b, omega_b(供 IMU + 调试)

    // ── 设备数组(plant + avionics 共同关心) ──
    std::vector<Engine>   engines;
    std::vector<Servo>    servos;
    std::vector<Fin>      fins;
    std::vector<ImuUnit>  imus;
    std::vector<GpsUnit>  gpss;
    std::vector<Icu>      icus;

    // ── 航电基础设施(bus + fcc 关心) ──
    bus::BusBuffer            bus;     // 局部线束副本,详见 §5
    std::optional<fcc::FccCore> fcc;   // 主箭有,分离后的子箭可能没有
};

} // namespace sim

> 强制约束#include 这个头文件的代码自动跨多个子域。这是 simulation/state/ 作为"合法跨域聚合层"的本质:只有这一层可以这样做


3. device-as-unified-entity:物理面 + 电子面共属一个实体

3.1 为什么不分离?

历史上有过两种错误的尝试:

方案问题
A. EngineMech 在 plant/,ECU 在 avionics/,两边各跑各的 FSM谁先推进?谁同步给谁?两个 FSM 阶段不一致就崩溃。需要复杂的同步协议。
B. 把 ECU 状态吃进 EngineMech 里plant 依赖 avionics → 倒挂;电子面在 HIL 模式下需要换实现,难以隔离

device-as-unified-entity 解法:每个设备是 RocketBody 的一个 struct,自身同时持有两面状态。两面的演化由 tick 函数同步推进

cpp
// simulation/pipeline/BodyTick.cpp 内部(精简)
for (auto& eng : body.engines) {
    // ECU 处理 Bus 命令 → 推进 EngineMech FSM → 输出 EngineEffect
    auto effect = avionics::device::ecu::step(
        eng.ecu,            // [in/out] 电子面
        eng.mech,           // [in/out] 物理面
        body.bus,           // [in/out] 总线
        *eng.spec,          // [const]  本构表
        body_env.aero,      // [const]  环境
        dt
    );
    out_engine_effects.push_back(effect);
}

两面同步由 step 函数一次性完成;调用者不感知"哪边先动"。

3.2 三种持有形态

设备类型持有原因
Engine / Servo / Finmech + 电子面有物理机械实体(活塞、舵面)
ImuUnit / GpsUnit只有电子面物理输入直接来自 body.aux / body.spatial,无独立机械实体
Icu只有电子面纯计时器/事件发生器,无物理实体

详见 03_Avionics_and_Bus/Device_Dual_Face.md


4. spec 指针的所有权契约

每个设备的 spec 字段是非拥有指针,指向 WorldEnv.engine_specs[...] 等数组的元素。

cpp
// 装配时(runtime::Assembler)
eng.spec = &world_env.engine_specs[type_id];   // 借用,不拥有

// 运行时(BodyTick)
double thrust = eng.spec->tables.thrust_vs_throttle(eng.ecu.throttle);

约束

  • WorldEnv 的生命周期必须严格长于 WorldStateruntime::SimulationInstance 保证)。
  • WorldEnv 装配完成后永不修改(spec 表不会被 realloc,指针稳定)。
  • 不允许把 spec 序列化进 RocketBody(深拷贝爆炸 + 失去单源)。

详见 WorldEnv_Assembly.md §6(指针稳定性约束)。


5. BusBuffer:局部线束副本

bus::BusBuffer 是 RocketBody 内部的总线缓冲——一个延时队列,供该 body 内的 device 互相通信:

            ┌─────────────────────────────────────────────────┐
            │  RocketBody[i]                                  │
            │                                                 │
            │   ┌─────┐    publish    ┌──────────┐            │
            │   │ ECU │ ─────────────▶│          │            │
            │   └─────┘               │          │            │
            │   ┌─────┐    publish    │ BusBuffer│            │
            │   │ SCU │ ─────────────▶│          │            │
            │   └─────┘               │          │            │
            │                         │          │            │
            │   ┌─────┐    poll       │          │            │
            │   │ FCC │◀───────────── │          │            │
            │   └─────┘               └──────────┘            │
            │                                                 │
            └────────────────┬────────────────────────────────┘
                             │ tick 末尾

                  ┌──────────────────────┐
                  │  bus::IBus           │  ← Runner 持有的外部总线
                  │  (InMemory / 1553B)  │
                  └──────────────────────┘

职责切割

  • RocketBody.bus(BusBuffer):body 内部 device-to-device + FCC 通信,由 sim::body_tick 演化。
  • bus::IBus(runtime 持有):跨 body 的总线、HIL 部署时的物理总线。由 sim::world_tick 末尾统一路由:把 BusBuffer 中 dst 不在本地的消息 push 到 IBus,从 IBus 拉外部消息进 BusBuffer。

> Wave 1 必删 的旧概念:BusChannel + SignalTag(broadcast 单源)。新模型只有 BusBuffer(local) + IBus(global)。

详见 03_Avionics_and_Bus/Semantic_Bus_Pattern.md §4 / §5。


6. world_stage:箭体级拓扑标记

world_stagedynamics::algebra::WorldStage 强类型 enum,标记该箭体当前的拓扑阶段(如 COMPOSITE_FLIGHT / STAGE_1_SPENT / CORE_COAST / PAYLOAD_ORBIT)。

用途

  1. Per-Stage 预编译派发CompiledDynamics.per_stage[body.world_stage] 选出该阶段的预编译 body pipeline(详见 05_Dynamics_Core/Topology_Algebra.md §5)。
  2. 拓扑代数演化evolve_topology(bodies, StageOp::TransitionOp{body_id, next}) 把单个 body 推进到下一阶段。
  3. 分离/分级SeparationOp 会从 source body 派生出 new body,各自有独立的 world_stage

与 FccStage 的关系

  • world_stage(箭体物理阶段)与 body.fcc->state.stage(FCC 内部控制相位)是两套独立代数,通过 DiscreteEvent + ICU 间接耦合。
  • 详见 05_Dynamics_Core/Topology_Algebra.md §4 / Blueprint §7.3。

7. 装配流程:从 YAML 到 RocketBody

data/input/rocket/falcon9.yaml
  + devices.yaml
  + 6 × engine_csv


runtime::Assembler::assemble()

  ├── load_environment_()       → WorldEnv.atmosphere / gravity / wind
  ├── load_plant_assets_()      → WorldEnv.engine_specs / servo_specs / ...

  └── instantiate_world_state_()

        └── for each body in mission.yaml:
              RocketBody body;
              body.body_id     = parse_body_id(...);
              body.world_stage = parse_initial_stage(...);

              body.spatial     = parse_initial_spatial(...);   // pos_lic = 0, vel_lic = 0
              body.inertial    = compute_initial_inertial(asset);
              body.aux         = {};                            // 零初值

              for each engine_layout_entry in body_yaml.engines:
                  Engine eng;
                  eng.id      = entry.id;
                  eng.install = entry.install;
                  eng.spec    = &world_env.engine_specs[type_id_lookup(entry.type)];
                  eng.mech    = plant::hardware::EngineMech::initial(*eng.spec);
                  eng.ecu     = avionics::device::ecu::EcuState::initial();
                  body.engines.push_back(std::move(eng));

              // 同样初始化 servos / fins / imus / gpss / icus
              // body.bus  默认空缓冲
              // body.fcc  仅主箭装配:std::optional<fcc::FccCore>::emplace(...)

              ws.bodies.push_back(std::move(body));

详见 07_Runtime/Assembler.md(待写)。


8. 与"composite hardware factory"模式的关系

部分文档可能把 RocketBody 的设备数组称为"composite hardware factory"。准确说法:

概念实质
Composite containerRocketBody 自身(struct 字段聚合)
Factoryruntime::Assembler::instantiate_world_state_()(一次性装配)
Pipelinesimulation/pipeline/factories/make_*_body_pipeline()(编译期组合 force computer)

三者职责不同

  • 容器只是 struct,无逻辑。
  • 装配工厂创建 instance,把 spec 指针接上。
  • Pipeline 工厂创建算子组合(thrust ⊕ aero ⊕ gravity),与 RocketBody 容器本身无关。

详见 05_Dynamics_Core/Topology_Algebra.md §5(per-stage 预编译 dynamics pipeline)。


9. 不变量与契约

契约强度检查点
body_idWorldState.bodies 中唯一必须Assembler 装配末尾
engines[i].spec 非空必须Assembler;BodyTick 入口断言
engines[i].id == spec->id(type 自洽)必须Assembler
spec 指针在 RocketBody 生命周期内稳定必须WorldEnv 装配后冻结
bus 在 BodyTick 内部可演化设计step 函数读写
fcc 在分离的 new_body 中可能为 nullopt设计TopologyOp::SeparationOp 创建 new_body 时显式决定
world_stage 切换只能由 evolve_topology 触发必须World tick 末尾,不可在 BodyTick 内修改
spatial / inertial / aux 只能由 dynamics_core integrator 演化必须BodyTick 物理阶段,不可在 avionics 阶段修改

10. 反模式

反模式后果正确做法
RocketBody 放在 dynamics/state/dynamics 必须依赖 plant/avionics/bus/fcc → universal 承诺破产放在 simulation/state/(Blueprint §2.6.1)
用继承表达不同 body(CoreBody : RocketBody)OOP 多态运行时;C-distillation 困难单一 struct + world_stage 枚举区分
每个 Engine 拥有自己的 EngineSpec 拷贝内存爆炸;spec 表多源 → 不一致只持有 const EngineSpec* 借用指针
BodyTick 中修改 body.world_stage拓扑演化语义破坏;并行不安全通过 DiscreteEvent 出栈,World 末尾统一 evolve_topology
bus::IBus* 塞进 RocketBodyRocketBody 不能在 HIL 模式 serialize;跨 body 路由耦合只持有 BusBuffer(local),IBus 在 runtime
BodyTick 末尾直接 world_state.event_history.push_back(...)越层 + 并行不安全写入 BodyLog.emitted_events,World 升维统一收集
engines[i].mech 在 avionics 子域内被修改跨域写入违反 device-as-unified-entity 约束统一通过 ecu::step(eng.ecu, eng.mech, ...) 一次性同步

11. C-Distillation 路径

C++ 抽象C 蜕化
std::vector<Engine>静态数组 + count(编译期上限)
std::optional<fcc::FccCore>fcc::FccCore + bool has_fcc
const plant::model::EngineSpec*uint32_t engine_spec_idx(索引到全局 spec 表)
contracts::EngineId(强类型 wrap)uint32_t typedef
嵌套 struct平铺到一个大 struct(cache 友好)
bus::BusBuffer(动态队列)环形缓冲(编译期容量)

关键:RocketBody 在 C 形态下应该是一个单一连续内存块,所有指针变为索引。这是 RT 部署的前提。

详见 09_Cross_Cutting/C_Distillation.md


12. 测试策略

12.1 单元层

  • Engine/Servo/Fin struct 默认构造测试。
  • EngineMech::initial(spec) 在不同 spec 下的初值正确性。

12.2 组件层

  • Assembler::instantiate_world_state_():给定 YAML,验证 ws.bodies 字段(数量、id、spec 指针匹配)。
  • spec 指针稳定性:装配后多次访问 engines[0].spec,地址不变。

12.3 集成层

  • 整循环跑 1s,验证设备状态与物理状态联合演化(如 ECU 点火 → EngineMech FSM → spatial 加速度上升)。

详见 08_Verification/Test_Strategy.md


13. 引用

  • Blueprint §2.6.1(复合状态容器)、§7.5(RocketBody 形态)、§7.16(迁移条目)
  • Dual_Layer_RWS.md(RocketBody 作为 BodyRWS 的 State)
  • WorldEnv_Assembly.md(spec 表归属与指针接线)
  • Body_World_Tick.md(device step 函数同步推进两面)
  • 03_Avionics_and_Bus/Device_Dual_Face.md(device-as-unified-entity 详解)
  • 03_Avionics_and_Bus/Semantic_Bus_Pattern.md §4–§5(BusBuffer vs IBus 切割)
  • 05_Dynamics_Core/Topology_Algebra.md(world_stage 演化代数)