Skip to content

PCR 架构重构方案 (PoC 落地版)

Context: FCC & Dynamics Simulation (C++20) Date: 2026-05-05 Scope: 针对多航电闭环、语义总线抽象以及阶段/拓扑演化的详细实施蓝图。


1. 顶层架构:配置组合与宇宙模型

1.1 装配公式

系统的终极装配由四个完全正交的维度决定: Run = Rocket × Mission × World × Deployment

  • Rocket: 物理资产与航电拓扑的装配清单(谁身上挂了 FCC,谁有发动机)。
  • Mission: 任务时序、动力学初始约束(如是否锁死在发射台)、分离事件定义。
  • World: 环境真相(大气、引力、发射场坐标)。
  • Deployment: 部署模式(SIL、Dyn-HIL、FCC-HIL)。Runner 根据此配置决定加载哪些配置、实例化哪些代码。

1.2 PoC 验证范围

  • Rocket: 二子级架构,包含:一子级(含中心与周边发动机)、二子级、整流罩、卫星载荷。
  • Mission: 包含点火、起飞、一子级关机、一二级分离、一子级返回点火 ‖ 二级入轨点火(并行)。
  • World: 维持现有不变。
  • Deployment: 仅实现 SIL(透明无损总线)。

1.3 World 顶层容器

World 维持作为系统最高层容器的设计,不新增 Universe 层。 分离发生后,原有的 COMPOSITE Body 裂变为多个新的 Body。 航电系统与 Body 是一对 0..1 的关系。一子级返回需要航电,溅落/整流罩则无航电。


2. Mission 事件与 per-body 阶段代数

动力学域的阶段(Stage)按 per-body 独立维护。

2.1 PoC 阶段枚举

Body可能的 Stage Tags
COMPOSITEprelaunch, engine_startup, power_ascend
STAGE1descent_ballistic, descent_powered
STAGE2coast_to_ignition, upper_ascend
FAIRING / SATELLITEballistic (唯一且最终状态)

2.2 核心事件流与触发源

Mission 事件触发源Body Stage 转移Hardware / Topology 变化
IGNITIONFCC 下发 IcuCmd(Ignite)prelaunch → engine_startup5 台发动机 FSM 进入 starting
LIFTOFF物理条件 (thrust > weight)engine_startup → power_ascend无硬件状态变化
STAGE1_SHUTDOWNFCC 下发 IcuCmd(Shutdown)不变 (仍在 power_ascend)周边发动机 running → stopping → idle
FIRST_STAGE_SEPFCC 下发 IcuCmd(FireBolt)COMPOSITE 解体,产生四个新 Body,各自进入初始 StageICU FSM armed → firing → done,产生拓扑突变事件
STAGE1_REENTRY_IGNITIONfcc1 下发 IcuCmddescent_ballistic → descent_powered一子级中心发动机 idle → starting → running
STAGE2_IGNITIONfcc2 下发 IcuCmdcoast_to_ignition → upper_ascend二子级发动机 idle → starting → running

> 关键解耦: ICU 状态机与物理 Stage 严格解耦。程序不做“当 Stage=X 时,发动机必须为 Y”的断言校验,此一致性完全由配置正确性保证。


3. 硬件状态与 ICU 状态机 (Mealy Machine)

硬件状态管理分为跨 tick 累积的长期状态和瞬时产出。

cpp
// 持久化状态
struct HardwareState {
    std::unordered_map<uint32_t, EngineFsm> engine_fsms; // {idle, starting, running, stopping}
    std::unordered_map<uint32_t, IcuFsm>    icu_fsms;    // {armed, firing, done}
    std::unordered_map<uint32_t, double>    fsm_timers;  // 延时与过程倒计时
};

// 瞬时状态 (由 Phase 1 产出,注入 Phase 2)
struct HardwareOutput {
    std::vector<EngineEffect> engine_effects;        // 推力输出
    std::vector<BusMessage> emitted_bus_messages;    // 上行遥测
    std::vector<TopologyEvent> topology_events;      // 延时结束后触发的物理断裂
};

4. 语义总线协议 (Bus ADT)

在 SIL 部署下,总线作为透明路由存在,但必须严格遵守三元组约束和时钟时间戳约束。

4.1 消息载体结构

cpp
struct BusMessage {
    uint32_t src;            // 信源地址
    uint32_t dst;            // 信宿地址
    Time     emitted_at;     // 发送端时钟
    BusMessagePayload payload; 
};

4.2 强类型 Payload 集合

消息体不得增减结构,保持贴近物理的原始传递:

cpp
// 1. IMU -> FCC: 传感器增量数据
struct ImuPayload {
    Vec3 delta_v_body;
    Vec3 delta_theta_body;
};

// 2. GPS -> FCC: 导航真值数据
struct GpsPayload {
    Vec3 pos_ecf;
    Vec3 vel_ecf;
};

// 3. FCC -> SCU: 连续控制 (直接提供摆角参与计算)
struct ScuPayload {
    struct EngineGimbalCmd { uint32_t slot_id; double pitch; double yaw; };
    std::vector<EngineGimbalCmd> commands;
};

// 4. FCC -> ICU: 离散时序触发指令
struct IcuPayload {
    enum class Cmd { IgniteEngines, ShutdownEngines, FireSeparationBolt };
    Cmd cmd;
    std::vector<uint32_t> targets; // 作用的槽位或螺栓地址
};

using BusMessagePayload = std::variant<ImuPayload, GpsPayload, ScuPayload, IcuPayload>;

4.3 SIL 透明总线抽象

cpp
class IBus {
public:
    virtual void publish(BusMessage msg) = 0;
    virtual std::vector<BusMessage> poll(uint32_t local_addr) = 0;
    virtual Clock& clock() = 0;
    virtual ~IBus() = default;
};

// SIL 模式:无队列堆积,同 tick 直接路由
class TransparentBus : public IBus { /* 内存 HashMap 路由 */ };

5. 分离事件与航电拓扑演化

由于火箭分离后可能会存在多个并行的航电闭环,我们需要定义在分离瞬间,子结构如何继承原有的航电状态。

cpp
struct TopologyEvent {
    BodyId source;                   // 如 COMPOSITE
    std::vector<BodySpawn> produces; // 如 STAGE1, STAGE2...
};

enum class AvionicsAction { 
    ActivateClone,  // 深拷贝原系统(例如 fcc1,获取了所有的积分导航变量)
    Continue,       // 原系统沿用(原 fcc,继续给 STAGE2 用)
    None            // 纯物理弹道(如 FAIRING)
};

struct BodySpawn {
    BodyId new_id;
    BodyStage::Tag initial_stage;
    AvionicsAction avionics;         
};

一子级返回时,一子级物理上携带的 FCC(fcc1)在分离事件时被激活,通过 ActivateClone 获取原组合体 FCC 的完整状态(偏置、导航位姿),随后二者在各自的总线上独立运行。


6. RuntimeTables (扁平化汇编输出)

彻底废除过度设计的 AssembledPlant 反射树。Runner 在启动期解析 YAML 后,生成极简的、全 uint32_t O(1) 寻址的静态运行期表格:

cpp
struct RuntimeTables {
    std::vector<EngineRuntime> engines; // 下标为 slot_id
    std::vector<IcuRuntime>    icus;
    std::vector<ImuRuntime>    imus;
};

前端 UI 若需展示,直接拉取并解析原始 YAML 文本;若需注入仿真修改,则生成 tmp.yamloverrides)重新执行 Assembler。


7. 数据流主循环 (The World Tick)

一个标准的 0.1ms (或者动态 dt) 的 Tick 流程:

text
1. 遍历所有带 AvionicsStack 的 Body:
    1a. FCC 唤醒: poll 传感器数据 -> Navigation/Guidance/Control -> publish SCU/ICU 指令
    1b. IMU/GPS 唤醒: 读取 Body 物理真值 -> publish IMU/GPS 遥测
    1c. ICU 唤醒: poll 指令 -> 推进 Mealy 状态机 -> 产出 (推力输出 & 拓扑突变事件)
    1d. SCU 唤醒: poll 指令 -> 更新推力管道的摆角

2. 遍历所有的 Body (无论是否有 Avionics):
    2a. 注入推力输出,计算 Thrust
    2b. 结合 World 大气,计算 Aero
    2c. 结合 World 引力,计算 Gravity
    2d. RK4 积分

3. 拓扑演化 (Apply Topology Events):
    3a. 消费所有的拓扑突变事件 (如 Separation)
    3b. 销毁源 Body,创建新 Body
    3c. 按 AvionicsAction 分配、深拷贝、或舍弃 FCC/Bus

本重构蓝图抛弃了臃肿的抽象,坚实地贴合了 PCR 分层原则,使得逻辑既满足底层 TDD/纯函数的干净约束,又可无缝扩展至任意半实物联合部署场景。