PCR 重构主蓝图(Master Blueprint)
> 本文件取代 _archive/ 下所有旧 Refactoring 文档。 > 本版本(v1.0)相对于初稿的关键修订: > - 引入 environment/ 模块,将"普适物理场"从 plant/ 中剥离 > - 引入 simulation/ 跨域绑定层,作为 RocketBody、WorldState、Probe、双层 RWS 实例化 的归宿 > - 重命名 dynamics/ → dynamics_core/,严格限定为 universal kernel(不依赖任何业务子域) > - runtime/ 退化为纯生命周期管理者,不再持有业务定义 > > 所有内容以四轮深度讨论后的最终裁定为准。推理溯源参见 PCR_Discussion_Log.md。
0. 阅读指引
| 章节 | 读者 | 核心问题 |
|---|---|---|
| §1 四视角架构 | 全员 | 系统里每一层各自"看到什么",闭环如何成立 |
| §2 目标结构 | 写新代码者 | 库划分、依赖图、环境/对象分离、跨域绑定层 |
| §3 Controller Protocol | FCC 开发者 | FCC 三层架构、GNC Mealy state、FccStage 代数 |
| §4 推力跨域数据流 | 动力学/航电开发者 | 推力计算五阶段在新架构中的归位 |
| §5 当前状态 | 全员 | 哪些可以直接用、哪些是脚手架、哪些必须从 main 迁回 |
| §6 迁移计划(四波) | 执行者 | Wave 0-3 各自的 scope / 步骤 / 试验守门 |
| §7 设计裁定 | 架构师 | 历史文档冲突的最终答案 |
| §8 工程约定 | 全员 | Scaffold 标记、commit 规范、TDD 四级体系 |
1. 四视角架构
理解这套系统最好的方式,是同时站在四个视角里看:FCC、Bus、Device、World。 每个视角只看自己能看到的东西。这种"被设计的无知"不是缺陷,是解耦的证明。
1.1 四个视角
FCC 视角:只看 FccInFrame(输入数据包)和 FccOutFrame(输出数据包)。 Bus 协议、设备型号、物理环境——一概不知。FccInFrame 本身已经封装了 Bus 的语义,它是一帧数据,不是原始总线帧。
Bus 视角:只看 (src_addr, dst_addr, payload) 三元组。 计算、状态、物理环境——一概不关心。Bus 是邮局:只投递信件,不阅读内容,不关心信件如何被使用。
Device 视角(IMU / GPS / Engine / Servo / ICU / Fin / FCC 等): 持有自身状态,接收来自 Bus 的输入或采样自身所在物理实体的状态,按 Mealy 状态机 演化,产出输出(写回物理实体或发到 Bus)。 Device 是物理实体的电子界面:它把"物理事实"翻译成"数字消息",把"数字命令"翻译成"物理驱动"。
World 视角:
- 驱动环境采样(
probe_trajectory/probe_aero/probe_mass_props) - 演化每个 body 内的物理实体(伺服机构、发动机、栅格舵)
- 演化每个 body 内的电子设备(SCU/ECU/ICU/IMU/GPS/FCC)
- 执行物理力学计算(thrust / aero / gravity)
- 积分;处理拓扑事件(StageOp)
- 真值反过来作为下一拍 Device 的物理输入
> 最关键的解耦:换发动机推力曲线 = 换发动机(plant/model 的 YAML 变了), > 但推力计算的 pipeline(plant/physics)一字不变。配方换了,流水线不变。 > > 更深一层的解耦:换地球 → 月球 = 换 environment/(无大气、不同重力场), > 但 plant/(火箭本身)和 dynamics_core/(积分引擎)都一字不变。舞台换了,演员和导演不变。
1.2 系统运行与闭环
重要澄清:AvionicsSystem 不是自闭环子系统。它必须与物理实体(伺服机构、发动机、栅格舵、IMU 物理元件、GPS 天线)共同构成完整闭环。
> 电气链路闭环:FCC → Bus → SCU → 电信号 → 回采到 SCU → Bus → FCC(仅数字层面) > > 完整闭环:上述 + ServoMech 真实摆角 + EngineMech 真实推力 + 大气环境 + 重力 + IMU 物理感知
所谓 "FCC 模飞测试" 也不是 Avionics 单跑——它是 Avionics + 静态 Plant: 物理实体被冻结在初始状态(伺服摆角=0,发动机推力=0,IMU 物理元件不动), 但物理实体仍然存在并被 Avionics 采样。
┌──────────────────────────────────────────────────────────────────────────┐
│ Composition Root (runtime::Runner) │
│ 读 run_xxx.yaml → assemble(rocket × mission × world × deployment) │
│ → simulation::Instance { WorldEnv(只读), WorldState(per-body 全状态) }│
└───────────────────────────┬──────────────────────────────────────────────┘
│ 启动期一次性装配
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ simulation::world_tick (per-body 并行,多速率) │
│ │
│ For each RocketBody: │
│ │
│ ① simulation::probe (Trajectory / Aero / MassProps) → BodyEnv │
│ │
│ ② simulation::body_tick (BodyEnv, RocketBody) ───────────────── │
│ ▸ Avionics: │
│ Engines: ECU 处理 Bus 命令 → 推进 EngineMech FSM → EngineEffect │
│ Servos: SCU 接 Bus 命令 → 推进 ServoMech 摆角 │
│ Fins: FinCtrl 接 Bus 命令 → 推进 FinMech │
│ IMUs: 从 body.aux 读真值 → 加噪/量化 → 发 Bus │
│ GPSs: 从 body.spatial 读真值 → 加噪 → 发 Bus │
│ ICUs: 接 Bus 命令 → 计时 → 到期发 DiscreteEvent │
│ │
│ ▸ FCC tick (仅当 fcc_dt 边界到达,e.g. 50Hz) │
│ decode Bus → FccInFrame → execute(strategy) → FccOutFrame → Bus │
│ │
│ ▸ plant::physics: thrust ⊕ aero ⊕ gravity (EngineEffect 已就绪) │
│ │
│ ▸ dynamics_core::ode (RK4) → 新 spatial / inertial / aux │
│ │
│ World 末尾: │
│ ③ 收集所有 body 的 DiscreteEvent → 解释为 StageOp → │
│ dynamics_core::algebra::evolve_topology(bodies, op) → 处理分离 │
└──────────────────────────────────────────────────────────────────────────┘1.3 三种部署/测试模式
| 模式 | 描述 | 信道实现 |
|---|---|---|
| avionics_dry(FCC 模飞测试) | 物理冻结,电气链路完整跑。验证 FCC 时序、调度、状态机、设备 FSM。 | InMemory + Plant 静态 |
| sil_monolithic | AvionicsSystem + PlantPhysics 同进程,全闭环 | InMemory(DynChannel + Bus 都内存) |
| hil_dyn | 物理在本机,FCC + Bus + Devices 在远端 | DynChannel = UDP;远端 Bus 内存或物理 |
| hil_fcc | FCC 在 RTOS,Devices + Bus + 物理在 SIL 仿真器 | DynChannel = 内存;Bus = 物理(1553B / TTE) |
HIL 的本质:不是"重新设计架构",而是在两个数据包边界上换信道。
DynChannel 跨 AvionicsSystem ↔ PlantPhysics(数据是 DynInFrame / DynOutFrame); Bus 跨 FCC ↔ Devices(数据是 BusMessage)。
2. 目标结构
2.1 库划分与依赖图
本项目的目录结构反映了一条核心架构哲学:universal kernel ↔ specific instantiation 的彻底分离。
runtime
│
▼
simulation ◄── 跨域绑定层
┌─────────┬─────┴────┬──────┬─────┬──────────┐
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
dynamics_core fcc avionics/ plant bus environment
│ │ devices │ │ │ │
│ │ │ │ │ │ │
└─────────┴────────┴───────┴─┴─────┴─────────┘
│
▼
contracts ← header-only 中立层
/ | \
/ | \
types frames monad ← 基础数学层核心约束(编译期强制):
contracts/是 header-only 的中立层,定义所有跨子系统数据包,无运行逻辑bus/与fcc/互不依赖(FCC 不知道 Bus 协议,Bus 不知道 FCC 算法)fcc/不依赖plant_*/dynamics_core//environment/(HIL 部署的前置条件)plant/physics/不依赖bus//fcc//avionics/(力学是纯函数)dynamics_core/不依赖任何业务子域(不依赖 plant / avionics / fcc / environment / bus)simulation/是唯一允许聚合所有子域的层(这是它存在的意义)runtime/只依赖simulation/(不持有业务定义,只做装配 + main loop)
| 库 | 目录 | 职责 | 命名空间 | 依赖 |
|---|---|---|---|---|
types | src/types/ | Vec3 / Matrix / Quat / Angle / Time / InterpTable1D | :: | — |
frames | src/types/frames/ | 坐标系定义、ECF/LIC/BODY 变换、WGS84 | frame:: | types |
monad | src/types/monad/ | RWS / Free / Writer 模板 | monad:: | types |
contracts | src/contracts/ | DynInFrame / DynOutFrame / FccInFrame / FccOutFrame / BusMessage / EngineEffect / DiscreteEvent + 强类型 ID | contracts:: | types + frames |
environment | src/environment/ | 普适物理场:Atmosphere / GravityField / WindField,关于位置/时间的纯函数 | env:: | types + frames |
plant/model | src/plant/model/ | 对象专属本构关系(静态规格):EngineSpec / ServoSpec / FinSpec / ImuSpec / GpsSpec / IcuSpec / BodyAsset / AeroSpec | plant::model:: | types + frames |
plant/physics | src/plant/physics/ | 力组装纯函数:compute_thrust / compute_aero / compute_gravity,只依赖 BodyEnv | plant::physics:: | types + frames + plant_model + contracts |
plant/hardware | src/plant/hardware/ | 物理机械面:EngineMech / ServoMech / FinMech + 演化函数 | plant::hardware:: | types + plant_model |
bus | src/bus/ | IBus / BusMessage / BusBuffer / SemanticInterpreter / 1553BInterpreter | bus:: | types + contracts |
avionics/devices | src/avionics/devices/ | 电子器件 step 函数:ecu / scu / icu / imu / gps / fin_ctrl | avionics::device:: | types + plant_model + plant_hardware + bus |
fcc | src/fcc/ | Free Monad DSL / Interpreter / GNC 算法纯函数 / FccStage 代数 | fcc:: | types + frames + monad + contracts |
dynamics_core | src/dynamics_core/ | Universal kernel:ODE + 积分器 + 拓扑代数 + RWS 模板。不知道火箭长什么样 | dynamics:: | types + frames + monad + contracts |
simulation | src/simulation/ | 跨域绑定层:RocketBody / WorldState / WorldEnv / BodyEnv 复合类型 + probe_* 函数 + 双层 RWS 实例化 + 多速率调度 | sim:: | 全部业务子域(合法聚合) |
runtime | src/runtime/ | Loader / Assembler / Runner / IDynChannel / PlantScope,生命周期管理 | runtime:: | simulation |
> 一票否决线(编译期强制): > - fcc/ 不得 #include 任何 plant/* / dynamics_core/* / environment/* 头文件 > - plant/physics/ 不得 #include 任何 bus/、fcc/、avionics/* 头文件 > - dynamics_core/ 不得 #include 任何 plant/* / avionics/* / fcc/ / bus/ / environment/ / simulation/ 头文件 > - simulation/ 是唯一允许 include 全部业务子域的层 > - contracts/ 无 .cpp 文件,纯 struct 定义
2.2 contracts 中立层
定义所有跨子系统对话用的数据包结构。Per-body 粒度,body_id 即 vector 下标。
// contracts/PerBodyFrames.h
// Avionics → Plant Physics
struct DynInFrame {
BodyId body_id;
std::vector<EngineEffect> engine_effects;
std::vector<FinDeflection> fin_deflections;
std::vector<DiscreteEvent> events;
};
// Plant Physics → Avionics(每个 body 一份,作为 Sensor 真值输入)
struct DynOutFrame {
BodyId body_id;
Vec3_T<frame::ECF> pos_ecf;
Vec3_T<frame::ECF> vel_ecf;
Quat_T<frame::LIC, frame::BODY> att_q;
Vec3_T<frame::BODY> omega_b;
Vec3_T<frame::BODY> spec_force_b;
double total_mass;
Vec3_T<frame::BODY> centroid_b;
};
// 边界对象:Engine FSM 的输出,喂给 thrust pipeline
struct EngineEffect {
EngineId engine_id;
double vacuum_thrust;
double mass_flow_fuel;
double mass_flow_ox;
double current_throttle;
Angle nozzle_pitch;
Angle nozzle_yaw;
};
struct DiscreteEvent {
enum class Kind { EngineShutdown, BoltFired, StageSeparation, FinDeploy };
Kind kind;
BodyId source_body;
uint32_t payload_id;
Time emitted_at;
};
struct BusMessage {
BusAddr src;
BusAddr dst;
Time emitted_at;
std::variant<ImuPayload, GpsPayload, ScuPayload, EcuPayload,
IcuPayload, FinCtrlPayload, TelemetryPayload> payload;
};
struct FccInFrame {
ImuPayload imu;
GpsPayload gps;
Time timestamp;
};
struct FccOutFrame {
ScuPayload scu;
IcuPayload icu;
EcuPayload ecu;
TelemetryPayload tlm;
};
// 强类型 ID(避免 uint32 互换)
enum class BodyId : uint32_t {};
enum class EngineId : uint32_t {};
enum class ServoId : uint32_t {};
enum class FinId : uint32_t {};
enum class ImuId : uint32_t {};
enum class GpsId : uint32_t {};
enum class IcuId : uint32_t {};
enum class BusAddr : uint32_t {};2.3 Environment:普适物理场
environment/ 装的是关于位置/时间的函数 f(x, t)。它不属于任何具体物体,但被所有物体感受到。
// environment/Atmosphere.h
namespace env {
struct Atmosphere {
// USSA1976 表 + 插值器
double pressure_at(double h_msl) const;
double density_at (double h_msl) const;
double sound_speed_at(double h_msl) const;
};
struct GravityField {
// WGS84 椭球重力场
Vec3_T<frame::ECF> gravity_at(Vec3_T<frame::ECF> pos_ecf) const;
};
struct WindField {
Vec3_T<frame::ECF> wind_at(Vec3_T<frame::ECF> pos_ecf, Time t) const;
};
} // namespace env> 设计原则:environment/ 的所有类对外只暴露纯函数(pressure_at, gravity_at …)。它内部可以持有插值表/CSV 数据,但不持有任何随时间演化的状态。 > > 替换地球→月球只改 env::Atmosphere::pressure_at 的数据源;不影响 plant/ 与 dynamics_core/。
2.4 Plant:对象本构关系
plant/ 装的是对象专属的查表关系。它代表了"这台火箭具体是什么"。
// plant/model/EngineSpec.h
namespace plant::model {
struct EngineSpec {
InterpTable1D thrust_curve; // 真空推力曲线
InterpTable1D mass_flow_curve;
double nozzle_exit_area; // Sa
std::array<double, 12> perturb_coeffs;
std::array<double, 4> stage_deviations;
// ...
};
struct AeroSpec {
InterpTable3D drag_table; // Cd(Mach, alpha, beta)
InterpTable3D lift_table;
InterpTable3D moment_table;
double reference_area;
double reference_length;
};
} // namespace plant::modelplant/physics/ 是纯力学函数,它接受 BodyEnv(局部上下文)和 RocketBody 状态,返回 Forces:
// plant/physics/Thrust.h
namespace plant::physics {
Forces compute_thrust(const sim::BodyEnv& env, const sim::RocketBody& body);
Forces compute_drag (const sim::BodyEnv& env, const sim::RocketBody& body);
Forces compute_gravity(const sim::BodyEnv& env, const sim::RocketBody& body);
} // namespace plant::physicsplant/hardware/ 装的是物理机械面(Mech)的定义与演化:
// plant/hardware/Mech.h
namespace plant::hardware {
struct EngineMech { double chamber_pressure; double true_thrust; double mass_flow; };
struct ServoMech { Angle actual_angle; Angle target_angle; double oil_pressure; };
struct FinMech { Angle actual_angle; };
void step_engine_mech(EngineMech&, EcuDriveCommand cmd, Time dt);
void step_servo_mech (ServoMech&, Angle target, Time dt);
void step_fin_mech (FinMech&, Angle target, Time dt);
} // namespace plant::hardware2.5 dynamics_core:Universal Kernel
dynamics_core/ 是通用推演引擎,它不知道火箭长什么样。它只知道:
- 给定一个
RHS function,怎么用 RK4 积分 - 给定一个
Body 集合 + StageOp,怎么演化拓扑 - 给定一个
Env / State / Log三元组,怎么组装 RWS pipeline
// dynamics_core/ode/EquationsOfMotion.h
namespace dynamics::ode {
// 通用:对任何有 spatial+inertial+aux 三状态的 body 做导数计算
template<typename Body>
auto compute_derivatives(const Body& body, const Forces& f) -> BodyDerivatives;
} // namespace dynamics::ode
// dynamics_core/ode/Integrator.h
namespace dynamics::ode {
template<typename Body, typename RhsFn>
Body rk4_step(const Body& state, RhsFn rhs, Time t, Time dt);
template<typename Body, typename RhsFn>
Body euler_step(const Body& state, RhsFn rhs, Time t, Time dt);
} // namespace dynamics::ode
// dynamics_core/algebra/StageOp.h
namespace dynamics::algebra {
enum class WorldStage : uint16_t {};
struct NoOp {};
template<typename BodyIdT> struct TransitionOp { BodyIdT body; WorldStage next; };
template<typename BodyIdT, typename EngineIdT>
struct SeparationOp {
BodyIdT source;
BodyIdT new_body;
std::vector<EngineIdT> detached_engines;
AvionicsAction avionics_for_new;
};
template<typename BodyIdT, typename EngineIdT>
struct IgnitionOp { BodyIdT body; EngineIdT engine; };
template<typename BodyIdT, typename EngineIdT>
using StageOp = std::variant<NoOp,
TransitionOp<BodyIdT>,
SeparationOp<BodyIdT, EngineIdT>,
IgnitionOp<BodyIdT, EngineIdT>>;
// 模板化的自由函数:算法在底层,类型在上层
template<typename Body>
std::vector<Body> evolve_topology(
const std::vector<Body>& bodies,
const StageOp<contracts::BodyId, contracts::EngineId>& op);
} // namespace dynamics::algebra
// dynamics_core/pipeline/Forces.h
namespace dynamics {
// Monoid: 多力相加自然组合
struct Forces {
Vec3_T<frame::BODY> force_b;
Vec3_T<frame::BODY> moment_b;
static Forces zero();
Forces operator+(const Forces& other) const;
Forces& operator+=(const Forces& other);
};
} // namespace dynamics
// dynamics_core/pipeline/RWSAlias.h
namespace dynamics {
template<typename Env, typename State, typename Log>
using RWS = monad::RWS<Env, Log, State>;
}> 关键测试:dynamics_core/ 的所有文件,grep -r "plant/\|avionics/\|fcc/\|bus/\|environment/\|simulation/" 必须 零命中。
2.6 Simulation:跨域绑定层
simulation/ 是这套架构的核心创新。它是唯一一层允许聚合所有业务子域的模块。它的存在解决了一个真实存在的架构压力:RocketBody 是跨域复合体,没有任何单一子域可以独占它。
simulation/ 的职责有四块:
2.6.1 复合状态容器 (simulation/state/)
// simulation/state/RocketBody.h
namespace sim {
// 物理状态原语(在 dynamics_core 中定义)
using dynamics::SpatialState;
using dynamics::InertialState;
using dynamics::AuxState;
struct Engine {
contracts::EngineId id;
plant::model::InstallParams install;
const plant::model::EngineSpec* spec;
plant::hardware::EngineMech mech; // 物理机械面
avionics::device::ecu::EcuState ecu; // 电子控制面
};
struct Servo { /* ... 同样的双面结构 ... */ };
struct Fin { /* ... */ };
struct ImuUnit { /* 只有电子面,物理输入是 body.aux */ };
struct GpsUnit { /* 只有电子面,物理输入是 body.spatial */ };
struct Icu { /* 纯电子,无物理输入 */ };
struct RocketBody {
contracts::BodyId body_id;
dynamics::algebra::WorldStage world_stage;
// ── 物理状态(dynamics_core 关心) ──
SpatialState spatial;
InertialState inertial;
AuxState aux;
// ── 设备(avionics + plant 关心) ──
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::BusBuffer bus;
std::optional<fcc::FccCore> fcc;
};
struct WorldState {
std::vector<RocketBody> bodies;
Time current_time;
};
} // namespace sim2.6.2 环境装配 (simulation/env/)
// simulation/env/WorldEnv.h
namespace sim {
// 严格只读 Reader,由 Assembler 装配后冻结
struct WorldEnv {
// ── 全局常量与坐标系 ──
frame::FrameConfig frame;
Constants constants;
// ── 普适物理场(来自 environment/) ──
env::Atmosphere atmosphere;
env::GravityField gravity_field;
env::WindField wind_field;
// ── 对象本构资产(来自 plant/model/,按 type_id 索引) ──
std::vector<plant::model::BodyAsset> body_assets;
std::vector<plant::model::EngineSpec> engine_specs;
std::vector<plant::model::ServoSpec> servo_specs;
std::vector<plant::model::FinSpec> fin_specs;
std::vector<plant::model::ImuSpec> imu_specs;
std::vector<plant::model::GpsSpec> gps_specs;
std::vector<plant::model::IcuSpec> icu_specs;
std::vector<plant::model::AeroSpec> aero_specs;
const plant::model::BodyAsset& get_asset(contracts::BodyId id) const;
};
// 局部 Body Reader:probe 函数的产物
struct BodyEnv {
// ── 当前 Body 的局部上下文切片 ──
TrajCtx traj; // 经纬高、速度方位角
AeroCtx aero; // 动压、马赫数、密度
MassPropsCtx mass_props; // 当前质量、质心、惯量
// ── 该 Body 引用的资产 ──
const plant::model::BodyAsset* asset;
const plant::model::AeroSpec* aero_spec;
// (engine_specs / servo_specs 通过 RocketBody.engines[i].spec 指针访问)
// ── 全局只读引用 ──
const env::Atmosphere* atmosphere;
const env::GravityField* gravity_field;
const env::WindField* wind_field;
};
} // namespace sim> WorldEnv 不持有任何运行时实例。设备实例属于 RocketBody。path_index(string→uint)只在 Assembler 内部局部使用,装配结束 RAII 析构。
2.6.3 探测器 (simulation/probe/)
Prober 函数是双层 RWS 之间的降维算子。它把全局只读 WorldEnv 与局部状态 RocketBody 结合,产出局部只读 BodyEnv。
// simulation/probe/Probe.h
namespace sim::probe {
TrajCtx probe_traj (const WorldEnv&, const RocketBody&, Time t);
AeroCtx probe_aero (const WorldEnv&, const RocketBody&, Time t);
MassPropsCtx probe_mass_props(const WorldEnv&, const RocketBody&, Time t);
// 一次性装配整个 BodyEnv
BodyEnv assemble_body_env(const WorldEnv&, const RocketBody&, Time t);
} // namespace sim::probe> 设计原则:plant/physics/compute_thrust 等纯函数只读 BodyEnv,不直接读 WorldEnv 大表。换大气模型只改 probe_aero,pipeline 不动。这就是"配方与流水线分离"在算子内部的实现。
2.6.4 双层 RWS 实例化与多速率调度 (simulation/pipeline/)
// simulation/pipeline/RWSTypes.h
namespace sim {
using BodyRWS = dynamics::RWS<BodyEnv, RocketBody, BodyLog>;
using WorldRWS = dynamics::RWS<WorldEnv, WorldState, WorldLog>;
} // namespace sim
// simulation/pipeline/BodyTick.h
namespace sim {
// 单体 tick:avionics → physics → integrate
BodyRWS body_tick(BodyEnv env, RocketBody body, Time dt);
}
// simulation/pipeline/WorldTick.h
namespace sim {
// 全局 tick:probe → body_tick × N → topology
WorldRWS world_tick(Time dt);
}
// simulation/pipeline/Scheduler.h
namespace sim {
// 多速率:FCC 50Hz vs Physics 1kHz vs Bus 同 tick
struct MultiRateScheduler {
Time fcc_period { Time::from_hz(50) };
Time phys_period { Time::from_hz(1000) };
Time last_fcc_tick;
bool should_fcc_tick(Time now) const;
};
} // namespace sim> 核心洞察:BodyRWS pipeline 本身是跨域的——它会调用 avionics、plant、dynamics_core 多个子域。因此它必须在 simulation/ 中实例化,而不是放在 dynamics_core(违反 universal 承诺)或某个具体子域(不能持有跨域调用)。
2.7 Runtime:生命周期管理
runtime/ 退化为非常薄的一层:它只负责程序的启动、装配、运行循环、退出。它不持有任何业务定义。
// runtime/Assembler.h
namespace runtime {
struct SimulationInstance {
sim::WorldEnv env; // 装配产物,冻结
sim::WorldState state; // 初始状态
PlantScope scope; // 部署切片配置
};
class Assembler {
public:
// 从一组 YAML 配置文件装配出完整的 SimulationInstance
SimulationInstance assemble(const std::string& run_config_path);
private:
// 装配期局部状态:path_index 在此 RAII,装配结束析构
std::unordered_map<std::string, uint32_t> path_index_;
// 阶段
void load_environment_(...); // → env::Atmosphere, GravityField, WindField
void load_plant_assets_(...); // → plant::model::*Spec
void instantiate_world_state_(...); // → sim::RocketBody × N
};
} // namespace runtime
// runtime/Runner.h
namespace runtime {
class Runner {
public:
int run(SimulationInstance instance);
private:
sim::MultiRateScheduler scheduler_;
std::unique_ptr<bus::IBus> bus_;
std::unique_ptr<IDynChannel> dyn_channel_;
};
} // namespace runtime
// runtime/IDynChannel.h
namespace runtime {
class IDynChannel {
public:
virtual void send(const std::vector<contracts::DynInFrame>&) = 0;
virtual std::vector<contracts::DynOutFrame> recv() = 0;
virtual ~IDynChannel() = default;
};
class InMemoryDynChannel : public IDynChannel { /* SIL */ };
class UdpDynChannel : public IDynChannel { /* HIL */ };
} // namespace runtime> 职责切割: > - simulation::Assembler 不存在(这是 runtime 的事) > - runtime 不定义 RocketBody、WorldEnv(这是 simulation 的事) > - Runner 不直接调用 dynamics_core::rk4(必须通过 sim::world_tick)
2.8 PlantScope:部署切片
Plant 物理资产唯一,PlantScope 决定本进程实例化什么:
// runtime/PlantScope.h
namespace runtime {
struct PlantScope {
bool physics_bodies;
bool sensor_truth;
bool actuator_effect;
bool fcc;
enum class TransportKind { InMemory, UdpServer, UdpClient };
TransportKind dyn_transport;
enum class BusKind { InMemory, Bridge1553B, RealtimeBus };
BusKind bus_kind;
std::vector<contracts::BusAddr> local_bus_addrs;
std::vector<contracts::BusAddr> remote_bus_addrs;
};
} // namespace runtimeYAML 示例:
# data/input/sim/deployments/sil_monolithic.yaml
plant_scope:
physics_bodies: true
sensor_truth: true
actuator_effect: true
fcc: true
dyn_transport: InMemory
bus_kind: InMemory2.9 World Stage 与拓扑代数
WorldStage 是火箭整体的飞行阶段(YAML 驱动),与 FccStage(FCC 自己内部状态)独立(详见 §3.7)。
// dynamics_core/algebra/StageOp.h
namespace dynamics::algebra {
enum class WorldStage : uint16_t {};
struct NoOp {};
struct TransitionOp { contracts::BodyId body; WorldStage next; };
struct SeparationOp {
contracts::BodyId source;
contracts::BodyId new_body;
std::vector<contracts::EngineId> detached_engines;
AvionicsAction avionics_for_new;
};
struct IgnitionOp { contracts::BodyId body; contracts::EngineId engine; };
using StageOp = std::variant<NoOp, TransitionOp, SeparationOp, IgnitionOp>;
// 自由函数(algorithm 在 dynamics_core,类型 contracts::* 来自 contracts)
template<typename Body>
std::vector<Body> evolve_topology(
const std::vector<Body>& bodies,
const StageOp& op);
} // namespace dynamics::algebra数据流:
ICU FSM → DiscreteEvent → DynInFrame.events
↓ sim::world_tick 末尾
sim::interpret_event(event) → StageOp
↓
dynamics::algebra::evolve_topology(bodies, op)ICU 不直接修改 World 状态,事件解释器在 simulation/ 中(simulation 决定一个 BoltFired 事件是变成 SeparationOp 还是 NoOp)。
2.10 信道抽象与 Bus
IBus 与 BusBuffer
IBus 是 FCC ↔ Devices 的语义信道,使用 algebra+interpreter 模式(因为有多种物理协议):
// bus/IBus.h
struct BusLog {
std::vector<RoutingTrace> traces;
std::vector<DropEvent> drops;
BusLog& operator+=(const BusLog& other); // Monoid
};
template<typename A>
using BusM = monad::Writer<BusLog, A>;
class IBus {
public:
virtual BusM<void> publish(contracts::BusMessage msg) = 0;
virtual BusM<std::vector<contracts::BusMessage>> poll(contracts::BusAddr local) = 0;
virtual void step_cycle(Time t) = 0;
virtual ~IBus() = default;
};
class InMemoryBus : public IBus {};
class Bridge1553BBus : public IBus {};
class RealtimeBus : public IBus {};BusMonitor 不再独立,融入 BusM<...> 的 BusLog。这通过 Writer Monad 自然汇聚到 World tick 的总日志。
bus::BusBuffer 是 RocketBody 内部的"局部线束"——一个延时队列副本,供该 body 内的 device 互相通信;不与外部 Bus 直接通信,由 sim::world_tick 末尾的总线路由阶段统一处理。
3. Controller Protocol(FCC)
FCC 是一个独立的 RWS 引擎,以自己频率运行(默认 50Hz),通过 FccInFrame / FccOutFrame 与外部对话。
3.1 三层架构
┌────────────────────────────────────────────────────────────────┐
│ Layer 1:Declarative Strategy (声明式策略) │
│ Free<FccOp, A> DSL │
│ 描述"做什么",不描述"怎么做"。无副作用。 │
│ read_imu() >>= run_nav >>= run_guide >>= run_ctrl >>= output │
│ 策略由 FccStage 决定:select_strategy(stage) → Free<FccOp, A> │
└────────────────────────────┬───────────────────────────────────┘
│ FccInterpreter::foldMap()
▼
┌────────────────────────────────────────────────────────────────┐
│ Layer 2:Imperative Interpreter (命令式解释器) │
│ 解释 FccOp 为 RWS 副作用 │
└────────────────────────────┬───────────────────────────────────┘
│ algorithms::nav_step / ...
▼
┌────────────────────────────────────────────────────────────────┐
│ Layer 3:Pure Functional Core (函数式核心) │
│ 纯 C++ 函数,接收输入返回输出,零副作用 │
└────────────────────────────────────────────────────────────────┘3.2 与 simulation 的契约
FCC 不直接依赖 bus::IBus 也不依赖 simulation/。FCC 只看 FccInFrame / FccOutFrame。 组装 / 拆解逻辑在 simulation/pipeline/ 中:
sim::body_tick 内:
Bus.poll(fcc.addr) → [BusMessage]
→ BusManager::decode_to_fcc_in
→ FccInFrame
→ fcc::tick()
→ FccOutFrame
→ BusManager::encode_from_fcc_out
→ [BusMessage]
→ Bus.publish(...)3.3 隔离保证
| 约束 | 原因 |
|---|---|
fcc/ 不 #include plant/* | FCC 不知道气动模型 / 发动机型号 |
fcc/ 不 #include dynamics_core/* | FCC 不知道积分器 / 拓扑代数 |
fcc/ 不 #include simulation/* | FCC 不知道仿真 orchestration(HIL 必备) |
fcc/ 不 #include bus/IBus.h | FCC IO 由 Interpreter 隔离 |
FccEnv 只含控制器配置 | Plant 有真实气动表,FCC 有 nominal 估计;两份独立 |
3.4 FccState(GNC 三模块 Mealy 机)
参见原 v0 文档 §3.6(内容不变,仅章节号下移)。
3.5 FccStage 状态机与 FccStageOp 代数
参见原 v0 文档 §3.7(内容不变,仅章节号下移)。
4. 推力跨域数据流
推力计算横穿多个域,每个阶段的归属与边界对象都已明确。
4.1 五阶段概览
Stage 1 配置加载
YAML(devices.yaml) + 6×CSV/engine
→ runtime::Assembler::load_plant_assets_()
→ sim::WorldEnv.engine_specs[type_id].tables
Stage 2 插值工具
InterpTable1D(线性插值 + 边界钳位)
→ types 层
Stage 3 发动机状态机(ECU + EngineMech) ← Avionics + Plant 边界
avionics::device::ecu::step(EcuState, EngineMech, bus, cfg, dt)
- 处理 Bus 命令(点火 / 关机)
- 推进 EngineMech FSM(OFF→STARTING(3s)→RUNNING→SHUTTING_DOWN(1s)→OFF)
- 查 spec.tables 计算 vacuum_thrust + mass_flow → EngineEffect
plant::hardware::step_engine_mech(EngineMech, ecu_drive, dt)
- 燃烧室压力、阀门开度的物理演化
→ contracts::EngineEffect 写入 DynInFrame
Stage 4 动力学推力管线 ← Plant Physics 边界
plant::physics::compute_thrust(BodyEnv, RocketBody) → Forces
三步链:
apply_perturbation() ← 12 系数摄动修正
compensate_pressure() ← 背压 F += Sa*(P0 - P_amb),其中 P_amb 来自 BodyEnv.aero
resolve_thrust_vectors() ← 喷管→体轴系,力矩 = lever × F_body
Stage 5 Forces Monoid 累加 ← dynamics_core
Forces::zero() + thrust + aero + gravity → dynamics::ode::rk4_step4.2 EngineEffect 边界
EngineEffect 是 Stage 3 → Stage 4 的边界对象,定义在 contracts/。 两侧通过 DynInFrame.engine_effects 传递,互不直接依赖。
可独立测试:
- bench:给 EcuState 喂点火指令,验证 EngineEffect.vacuum_thrust 时序与 CSV 表吻合
- bench:给 thrust pipeline 喂固定 EngineEffect,验证力/力矩数值
- subsystem:两者串联跑 3s 起动 + 稳态 + 关机,验证力的时间积分
4.3 probe_aero:局部 Context 解耦推力与大气
plant::physics::compute_thrust 不直接读 env::Atmosphere。它读 BodyEnv.aero.static_pressure,由 sim::probe::probe_aero 在 BodyRWS pipeline 开头一次性算好:
// simulation/probe/Probe.cpp
AeroCtx probe_aero(const WorldEnv& env, const RocketBody& body, Time t) {
return AeroCtx {
.static_pressure = env.atmosphere.pressure_at(body.spatial.h_msl()),
.density = env.atmosphere.density_at (body.spatial.h_msl()),
.mach = mach_from(body.aux.spec_force_b, body.spatial.vel_lic),
.dyn_pressure = 0.5 * density * v_squared,
};
}
// plant/physics/Thrust.cpp
Forces plant::physics::compute_thrust(const sim::BodyEnv& env, const sim::RocketBody& body) {
double P_amb = env.aero.static_pressure; // 不直接知道 env::Atmosphere 的存在
// ...
}这个模式让 compute_thrust 不知道 env::Atmosphere 是什么,只看一个数值 static_pressure。 换大气模型只改 probe_aero,pipeline 不动。
同样的局部 Context 模式应用到:
TrajCtx:经纬高、速度方位角MassPropsCtx:当前质量、质心、惯量
每个 BodyRWS 算子只读对应的 Ctx,不直接读 WorldEnv 大表。
5. 当前状态(迁移基线)
5.1 ✅ 正确落实,不要动
| 位置 | 内容 |
|---|---|
src/types/ | Vec3_T<Frame> / Matrix / Quat / Time / Frames.h |
src/types/monad/RWS.h | RWS 单子 / Free / Writer 模板 |
src/dynamics/ode/EquationsOfMotion.cpp | RK4 导数 / pack/unpack / 欧拉方程 / 四元数导数(待 rename 至 dynamics_core/) |
src/dynamics/ode/Integrator.cpp | 纯数值 RK4 / Euler(待 rename) |
src/dynamics/state/Forces.h | Monoid(待 rename 至 dynamics_core/pipeline/) |
src/contracts/PerBodyFrames.h | Wave 0 已建 |
src/avionics/devices/*.h | Wave 0 已建骨架 |
src/plant/hardware/Mech.h | Wave 0 已建 |
5.2 🟡 结构落地,逻辑空壳
| 位置 | 现状 | 需要做 |
|---|---|---|
src/bus/{IBus,BusPayloads,TransparentBus}.h | include 路径错误,编不过 | 修 include 路径 |
src/plant/model/ | 仅有 BodyAsset.h 与若干迁移的 config 头 | 补 EngineSpec / ServoSpec / FinSpec / ImuSpec / GpsSpec / IcuSpec |
src/plant/hardware/ | Mech.h 已建,旧的 OOP 模型类待删 | Wave 1 迁入 step_engine_mech / step_servo_mech / step_fin_mech |
src/avionics/devices/ | 骨架已建,所有 step 函数为空 | Wave 2 实现 step 函数 |
src/dynamics_core/ | 不存在 | Wave 0 创建并迁入原 dynamics/ 内容 |
src/simulation/ | 不存在 | Wave 0 创建,引入 RocketBody/WorldState/WorldEnv/BodyEnv/Probe/Pipeline |
src/environment/ | 不存在 | Wave 0 创建,迁入大气/重力/风场模型 |
src/runtime/ | 含 WorldEnv.h 等业务定义 | Wave 0 清空业务定义,只留 Assembler/Runner/IDynChannel |
src/main/closed_loop_10s.cpp | 弹道骨架,无 FCC | Wave 3 改名 + 新建 closed_loop_full.cpp |
5.3 ❌ 关键逻辑降级,必须从 main 迁回
| 模块 | main 中 | pcr-arch 占位 |
|---|---|---|
FlightStateProber | pipeline/FlightStateProber.cpp(104 行) | plant/physics/FlightStateProber.cpp(61 行) |
Drag | pipeline/Drag.cpp(137 行,上升/下降双模) | plant/physics/Drag.cpp(44 行) |
Thrust | pipeline/Thrust.cpp(158 行,三阶段) | plant/physics/Thrust.cpp(46 行) |
| Engine/Servo/ICU FSM | rocket/device/DeviceOperators.cpp(102 行) | plant/hardware/DeviceOperators.cpp(19 行) |
| FCC 全家桶 | src/fcc/{free_monad,navigation,guidance,control,stage}/ | src/fcc/FccCore.cpp(7 行 stub) |
5.4 关键迁移归位表(v1.0 修订)
| 当前位置 | 迁移目标 | Wave |
|---|---|---|
src/dynamics/ode/ | src/dynamics_core/ode/ | Wave 0 |
src/dynamics/algebra/ | src/dynamics_core/algebra/(class → 模板化自由函数) | Wave 0 |
src/dynamics/state/Forces.h | src/dynamics_core/pipeline/Forces.h | Wave 0 |
src/dynamics/state/SpatialState.h<br>src/dynamics/state/InertialState.h<br>src/dynamics/state/AuxState.h | src/dynamics_core/state/*(物理状态原语) | Wave 0 |
src/dynamics/state/RocketBody.h(device-as-unified-entity) | src/simulation/state/RocketBody.h | Wave 0 |
src/dynamics/state/WorldState.h | src/simulation/state/WorldState.h | Wave 0 |
src/dynamics/state/BodyEnv.h | src/simulation/env/BodyEnv.h | Wave 0 |
src/runtime/WorldEnv.h | src/simulation/env/WorldEnv.h | Wave 0 |
src/plant/model/AeroConfig.h<br>src/plant/model/InertialConfig.h 等 | 保留原位(plant/model/) | 不动 |
src/dynamics/PhysicalRegistry.* | src/simulation/pipeline/BodyTick.* + WorldTick.* | Wave 1 |
src/dynamics/pipeline/* | src/plant/physics/ | Wave 1 |
src/plant/model/(包含的大气/重力配置) | 拆出至 src/environment/ | Wave 0 |
src/runtime/loader/* | 保留原位 | 不动 |
src/dynamics_core/ (旧目录,含 StageMachine.h) | 删除(合并入新 dynamics_core/algebra/) | Wave 0 |
src/plant_model/ | 删除(合并入 src/plant/model/) | Wave 0 |
6. 迁移计划(四波)
Wave 0 · 目录正骨 + 结构定型(无逻辑变化)
v1.0 重大调整:本 Wave 的核心是目录拓扑的全面对齐。在没有正确的目录骨架之前,任何 CMake 编译都是有害的。
6.0.1 目录正骨
删除遗留平行目录:
src/plant_model/→ 合并入src/plant/model/src/dynamics_core/(旧)→ 删除src/plant/interface/→ 删除(职责移交contracts/)src/dynamics/pipeline/→ 内容迁至src/plant/physics/
创建新顶层目录:
src/environment/:装Atmosphere.h、GravityField.h、WindField.hsrc/dynamics_core/:装 universal kernel(ode + algebra + pipeline + state 原语)src/simulation/:装跨域绑定层(state + env + probe + pipeline)
迁移现有内容:参见 §5.4 表。
创建
types/frames/子目录:把 phantom types 从types/Frames.h析出。
6.0.2 CMake 库划分
每个顶层模块对应一个 CMake target:
# src/contracts/CMakeLists.txt
add_library(contracts INTERFACE) # header-only
target_include_directories(contracts INTERFACE ...)
# src/environment/CMakeLists.txt
add_library(environment STATIC ...)
target_link_libraries(environment PUBLIC types frames)
# src/dynamics_core/CMakeLists.txt
add_library(dynamics_core STATIC ...)
target_link_libraries(dynamics_core PUBLIC types frames monad contracts)
# 禁止 link plant/avionics/fcc/bus/environment/simulation
# src/simulation/CMakeLists.txt
add_library(simulation STATIC ...)
target_link_libraries(simulation PUBLIC
types frames monad contracts
environment plant_model plant_physics plant_hardware
avionics_devices bus fcc
dynamics_core)
# src/runtime/CMakeLists.txt
add_library(runtime STATIC ...)
target_link_libraries(runtime PUBLIC simulation)6.0.3 自底向上的"逐层编译"循环
正确目录建好后,按依赖图自底向上编译:
顺序:
1. types + frames + monad → 单独编译验证
2. + contracts → 加入
3. + environment + plant/model + plant/hardware
4. + plant/physics
5. + bus
6. + avionics/devices
7. + fcc
8. + dynamics_core
9. + simulation
10. + runtime每一步保持上面 N 个库链接通过,下面所有目录在 CMake 中暂时 add_subdirectory 注释掉。这避免遗留代码的报错污染当前层的排错。
6.0.4 状态结构最终定型(无逻辑实现)
dynamics_core/algebra/:删 class,改模板化自由函数evolve_topology<Body>dynamics_core/algebra/StageOp.h:enum class WorldStage : uint16_t {}simulation/state/RocketBody.h:device-as-unified-entity 形态(已在 Wave 0 前完成原型)simulation/env/WorldEnv.h:严格只读,含 environment + plant assetssimulation/probe/Probe.h:声明probe_aero / probe_traj / probe_mass_propsbus/{IBus,BusPayloads,TransparentBus}.h:修 include 路径bus/BusChannel.h+SignalTag:加[[deprecated]],Wave 2 删除
Wave 0 守门:
- 全量编译通过(10 个 CMake target 全绿)
- 现有所有测试至少通过编译(功能可能 stub,但必须能 link)
grep -r "plant/\|avionics/\|fcc/\|bus/\|environment/\|simulation/" src/dynamics_core/→ 零命中
Wave 1 · Plant Physics 内核恢复
目标:simulation::body_tick 在相同 BodyEnv 输入下,与 main 力/力矩输出相对误差 ≤ 1e-6。
准备:生成 main golden trajectory 作为回归基线。
迁移顺序:
plant::physics::FlightStateProber从 main 迁入plant::physics::compute_drag从 main 迁入(上升/下降双模 + 完整 6 分量)plant::physics::compute_thrust从 main 迁入(三阶段 + EngineForceState)plant::physics::compute_gravity加 ECF→LIC 旋转sim::probe::probe_aero / probe_traj / probe_mass_props实现(替代旧BodyEnv直读WorldEnv大表的模式)sim::body_tick雏形:probe → forces → integrate
守门(bench + trajectory 级):
| 试验 | 类别 | 期望 |
|---|---|---|
bench_prober_static_sea_level | bench | 4 气动角全 0,q=0 |
bench_prober_above_karman | bench | dyn_pressure=0,mach=0 |
bench_drag_ascending_subsonic | bench | F_x ≈ -Cd·S·q |
bench_drag_descending_gridfin | bench | 含力矩 |
bench_thrust_perturbation | bench | 公式验证 |
bench_thrust_pressure_compensation | bench | ΔF = Sa·101325 |
bench_thrust_off_axis_engine | bench | 旋转方向 |
trajectory_physics_vs_main | trajectory | 力/力矩对 main 相对差 ≤ 1e-6 |
Wave 2 · 设备层 + Avionics 闭环
目标:每个 device 独立工作(fidelity=Transparent),FCC 模飞测试跑通。
- Plant Hardware 演化(
step_engine_mech/step_servo_mech/step_fin_mech)从 main 迁入 - Device step 函数 完整实现:
ecu::step(含 EngineFSM)scu::step(采样真实摆角)fin_ctrl::stepimu::step(从 body.aux 读真值)gps::step(从 body.spatial 读真值)icu::step(计时器,到期发 DiscreteEvent)
- Bus 单源化:删
BusChannel.h+SignalTag;BusBuffer 挂在 RocketBody sim::body_tick完整化:avionics step → physics → integrate- DiscreteEvent 解释器(
sim::world_tick末尾把 events 翻译成 StageOp)
守门:见原 v0 §6 Wave 2 表(内容不变)。
Wave 3 · FCC + Mission + 全闭环
- FCC 复活:
git show main:src/fcc/...整组迁回(不重写)- 仅适配
FccInFrame从 BusMessage 组装(由sim::body_tick内的 BusManager 完成)
- 仅适配
- FccStage 代数:
fcc/stage/FccStageOp.h,evolve_fcc_stage自由函数 - stages.yaml 加载:
runtime::Assembler解析为FccEnv.stage_table[] - Mission YAML:
data/input/missions/nominal_ascent.yaml - Deployment YAML:
sil_monolithic.yaml+hil_dyn.yaml+hil_fcc.yaml closed_loop_10s.cpp改名ballistic_smoke_test.cpp;新建closed_loop_full.cpp
守门:见原 v0 §6 Wave 3 表(内容不变)。
7. 设计裁定
7.1 Bus:bus::IBus 唯一,BusMonitor 用 Writer Monad 内化
vtable + 强类型 payload variant。删 BusChannel.h + SignalTag。BusMonitor 不独立存在,融入 BusM<...> 的 BusLog。
7.2 Stage 代数:模板化自由函数 + 强类型 enum
dynamics::algebra::evolve_topology<Body>(bodies, op)模板化自由函数- 算法在
dynamics_core/algebra/,类型contracts::BodyId来自contracts/ enum class WorldStage : uint16_t {}强类型
7.3 FccStage 与 WorldStage 独立 + 双代数化
WorldStage在dynamics::algebra,由StageOp演化FccStage在fcc::stage,由FccStageOp演化- 两者通过 DiscreteEvent + ICU 间接关联
7.4 Device 不使用 algebra+interpreter 模式
| 层 | algebra+interpreter? | 原因 |
|---|---|---|
| Bus | ✅ 用 | 多消息类型共信道 + 多物理协议 |
| FCC | ✅ 用 | 策略 YAML 驱动 + Sim/RTOS 部署需不同 interpreter |
| Device | ❌ 不用 | 操作集合固定 + fidelity 是局部决策 |
7.5 RocketBody 形态:device-as-unified-entity + 跨域归属
每个 device 是 RocketBody 的一个完整实体,自身同时持有 mech(如有)+ 电子面。 RocketBody 整体归属于 simulation/state/,不归属于任何单一业务子域。
7.6 强类型 ID 与 enum class
BodyId / EngineId / ServoId / FinId / ImuId / GpsId / IcuId / BusAddr用enum class : uint32_t包装- 所有 stage / phase / fidelity / algo 用
enum class : uint16_t
7.7 WorldEnv 严格只读 + Path Index 装配期 RAII
sim::WorldEnv 只持有 environment 引用 + 静态资产规格 + 全局常量。 path_index 在 runtime::Assembler 内部局部存在,装配结束析构。
7.8 update_inertial_properties 删除
质量 / 质心 / 惯量每 tick 在 sim::probe::probe_mass_props 中从 BodyAsset.inertia_table 查表。
7.9 AssembledPlant / RuntimeTables 反射树废弃
前端自读 YAML 构建数据结构,C++ 端无需可序列化反射树。 "AssembledPlant" 退化为 runtime::SimulationInstance 的非正式代称。 WorldEnv 内部 vector<T> + uint32 索引即是"运行时表"实现,不引入 RuntimeTables 名词。
7.10 AvionicsState/Env 不存在
不作为独立结构。Avionics 组件状态全部进 RocketBody(即 simulation/state/)。 没有 avionics_step(AvionicsEnv, AvionicsState) 这个签名。
7.11 DynInFrame / DynOutFrame per-body
不是 world 级。sim::world_tick 接受 / 产出 vector<DynInFrame> 和 vector<DynOutFrame>,body_id 即 vector 下标。
7.12 C-Distillation 软化
后续目标,不约束当前设计细节。 PoC 阶段优先"干净的语法抽象 + 清晰的隔离",性能优化后置。
7.13 FCC 模飞测试是一等公民
avionics_dry_flight 是独立测试场景,对应物理冻结、电气链路完整跑通。
7.14 AvionicsSystem 不自闭环
AvionicsSystem 必须与物理实体共同闭环。
7.15 【v1.0 新增】Environment 与 Plant 严格分离
environment/ 装的是关于位置/时间的纯函数(场),不属于任何具体物体。 plant/ 装的是对象专属的本构关系(查表)。 两者通过 sim::WorldEnv 一起被聚合,但它们的版本独立、变化频率独立、可替换性独立。
7.16 【v1.0 新增】dynamics_core 严格普适
dynamics_core/ 是 universal kernel,不知道火箭长什么样。 它只能 #include types / frames / monad / contracts。 禁止 #include 任何业务子域(plant / avionics / fcc / bus / environment / simulation)。
7.17 【v1.0 新增】simulation 是跨域绑定的唯一合法层
RocketBody 是跨域复合体(同时被 dynamics_core / plant / avionics / bus / fcc 操作)。 它不能归属于任何单一子域,否则违反对应子域的"无知性"承诺。 simulation/ 是唯一允许聚合所有业务子域的层,专门承担这种跨域复合体的归属。
7.18 【v1.0 新增】runtime 退化为纯生命周期管理
runtime/ 不持有业务定义(不定义 RocketBody / WorldEnv)。 它只做三件事:
- YAML 解析(Loader)
- 装配(Assembler):构造
sim::WorldEnv与初始sim::WorldState - 运行(Runner):调用
sim::world_tick的 main loop
7.19 【v1.0 新增】BodyEnv 由 Probe 显式生成
双层 RWS 的咬合靠 sim::probe::* 函数。 plant::physics::* 只读 BodyEnv,不直接读 WorldEnv。 换 environment / 换 plant assets 都只影响 probe 实现,不影响 physics pipeline。
8. 工程约定
8.1 Scaffold 标记
// SCAFFOLD: <required-test-name> [Wave-N]
// 原因:xxx
// 预期替换:yyy
double engine_mass = 50.0; // SCAFFOLD: bench_inertia_table_lookup [Wave1]每完成一个 Wave,对应 scaffold 测试转绿,git grep 'SCAFFOLD:' src/ | wc -l 严格下降。
8.2 commit 规范
[wave-N][module] 简要说明
migrated from: src/dynamics/pipeline/Drag.cpp@main
ns: dynamics:: → plant::physics::
delta vs main: <有意识的修改列表>
test: bench_drag_ascending_subsonic ✓
scaffolds: 5 → 48.3 TDD 四级体系(火箭试验语义)
不以函数签名级 assert 为导向,以"试验任务书"为导向。
| 级别 | 目录 | 对应工程实践 |
|---|---|---|
| bench | tests/bench/ | 单机台架试验 |
| subsystem | tests/subsystem/ | 子系统联试 |
| trajectory | tests/trajectory/ | 6DoF 仿真回归 |
| mission | tests/mission/ | 任务场景 / 飞行包络 |
| fault | tests/fault/ | 容错注入 |
| golden | tests/golden/ | 参考数据集 |
目录结构
tests/
├── bench/
│ ├── device/ # IMU / GPS / SCU / ECU / ICU / Fin Ctrl
│ ├── physics/ # Drag / Thrust / Gravity / Prober
│ ├── hardware/ # EngineMech / ServoMech / FinMech
│ ├── fcc/ # FccStage 演化 / GNC 算法
│ ├── environment/ # Atmosphere / Gravity / Wind 查表
│ ├── simulation/ # Probe / BodyTick / WorldTick
│ └── bus/ # IBus 路由
├── subsystem/
│ ├── avionics_dry/ # 模飞测试(电气闭环 + 物理冻结)
│ └── physics_open/ # 弹道联调(物理闭环 + FCC 旁路)
├── trajectory/ # 全链路数值回归
├── mission/ # 场景级
├── fault/ # 故障注入
└── golden/
└── main_baseline_t10s.csv8.4 配置目录最终裁定
data/input/
├── world/ # 环境真相(environment/ 的数据源)
│ ├── earth.yaml
│ └── atmosphere.csv
├── rocket_v1/ # 物理资产真相(plant/model/ 的数据源)
│ ├── rocket.yaml
│ ├── devices.yaml
│ ├── aero/
│ ├── mass/
│ └── engine/
├── fcc_v1/ # 飞控软件配置
│ ├── fcc.yaml
│ ├── control.yaml
│ ├── guidance.yaml
│ ├── program_att/
│ └── stages.yaml
├── missions/ # 任务剖面
│ └── nominal_ascent.yaml
└── sim/
├── deployments/
│ ├── sil_monolithic.yaml
│ ├── hil_dyn.yaml
│ └── hil_fcc.yaml
└── runs/
└── run_001.yaml附录:v0 → v1.0 章节迁移索引
| v0 章节 | v1.0 章节 | 变更 |
|---|---|---|
| §2.1 库划分(11 库) | §2.1(13 库) | 新增 environment / simulation;dynamics 改名 dynamics_core |
| §2.2 contracts | §2.2 | 不变 |
| §2.3 RocketBody | §2.6.1 | 迁至 simulation/state/ |
| §2.4 WorldEnv | §2.6.2 | 迁至 simulation/env/;引用 environment/ |
| §2.5 PlantScope | §2.8 | 章节号下移 |
| §2.6 World Stage 代数 | §2.9 | algebra 迁至 dynamics_core/algebra/,模板化 |
| §2.7 IBus | §2.10 | 章节号下移 |
| 无 | §2.3 Environment | 【新增】 |
| 无 | §2.4 Plant | 详细展开 model/physics/hardware |
| 无 | §2.5 dynamics_core | 【新增】 |
| 无 | §2.6 Simulation | 【新增】跨域绑定层完整描述 |
| 无 | §2.7 Runtime | **【新增】**生命周期管理层 |
| §4 Thrust dataflow | §4 | 路径调整 |
| §5 当前状态 | §5 | 增补 v1.0 迁移归位表 |
| §6 Wave 0 | §6 Wave 0 | 重写:目录正骨 + CMake 库划分 + 逐层编译 |
| §7.1-7.15 | §7.1-7.14 | 章节号微调 |
| 无 | §7.15-7.19 | 【新增】 environment/plant 分离、dynamics_core 普适、simulation 跨域绑定、runtime 退化、BodyEnv probe |