Lambert 求解与 porkchop 扫描

Lambert 问题给定两端位置与飞行时间,求连接两点的二体轨道。本页介绍三部分: 二体 Lambert 求解器(Rust Izzo 内核)、porkchop 扫描,以及以二体解为初猜的 CR3BP 三体打靶。

二体 Lambert 求解器

solve_lambert() 解单次二体 Lambert 问题, 算法为 Izzo (2015),内核用 Rust 实现(e2m2e-propagation crate), 经 e2m2e._integrators 暴露,Python 侧只做类型转换与结果封装。

from e2m2e.algorithm.transfer import solve_lambert

# Vallado 经典算例
r0 = [5000.0, 10000.0, 2100.0]      # km
rf = [-14600.0, 2500.0, 7000.0]     # km
tof = 3600.0                        # s
mu = 398600.4418                    # 地球 GM,km³/s²

sol = solve_lambert(r0, rf, tof, mu)
print(f"出发速度: {sol.v0}")        # km/s
print(f"到达速度: {sol.vf}")
print(f"迭代次数: {sol.n_iter}")

direction 选择转移角方向:"short" 表示转移角 < π(默认),"long" 表示 > π。 revs 指定完整圈数,多圈时返回右分支(低能)解;飞行时间低于该圈数 最小转移时间时抛 ValueError

返回的 LambertSolution 含出发速度 v0 与 到达速度 vf,均为 (3,) 向量,单位 km/s;另有迭代次数 n_iter 与圈数 revs

批量求解

solve_lambert_batch() 对 N 组几何 × M 个飞行时间 的网格批量求解,一次调用进入 Rust 内核:

import numpy as np
from e2m2e.algorithm.transfer import solve_lambert_batch

r0_list = np.tile(r0, (4, 1))       # (N, 3)
rf_list = np.tile(rf, (4, 1))       # (N, 3)
tofs = [3600.0, 7200.0, 10800.0]    # (M,)

out = solve_lambert_batch(r0_list, rf_list, tofs, mu)
# out 形状 (N, M, 2, 3):[..., 0, :] 为 v0,[..., 1, :] 为 vf

无解的组合(如弦长为零)对应位置填 NaN,不影响其余组合。

porkchop 扫描

porkchop() 在出发时间 × 飞行时间网格上逐点解 Lambert 问题,得到双脉冲 ΔV 网格,即 porkchop 图的数据层。出发与到达终端 经 TerminalCondition 接口提取状态 (如 OrbitTerminal,或自定义实现), 本函数不关心状态如何产生。

对网格点 (t_dep, tof):出发终端状态取 t_dep 时刻,到达终端状态取 t_dep + tof 时刻,脉冲为转移速度与终端轨道速度之差。

import numpy as np
from e2m2e.algorithm.transfer import porkchop

t_dep = np.linspace(0.0, 3600.0, 20)        # 出发时间网格,s
tof = np.linspace(900.0, 5 * 3600.0, 30)    # 飞行时间网格,s

data = porkchop(dep, arr, t_dep, tof, mu=398600.4418, dynamics=None)

print(data.dv1.shape)     # (20, 30),出发脉冲,km/s
print(data.dv2.shape)     # 到达脉冲
print(data.total.shape)   # 总脉冲 dv1 + dv2

# 画总 ΔV 等值线图
ax = data.plot()

其中 dep / arr 是实现 TerminalCondition 的终端对象; 解析终端(如圆轨道)不依赖动力学传播时 dynamics 可传 None。 返回的 PorkchopData 携带三个网格与时间轴, plot() 直接用 matplotlib 画等值线。

三体打靶 ThreeBodyLambert

二体 Lambert 解忽略第三体引力,在地月空间只是初猜。 ThreeBodyLambert 以二体解为初猜, 在 CR3BP 动力学下用阻尼 Newton 打靶修正出发速度,使给定飞行时间后的末端 位置命中目标:

  1. 两端物理状态 (km, km/s) 经 CR3BP_System.physical_to_dimensionless 无量纲化

  2. 无量纲几何上以 μ = 1 调 solve_lambert 得初猜出发速度

  3. Newton 迭代:传播 with_stm=True,取末端 STM 的 Φ_rv 块(Φ[0:3, 3:6]) 解修正量;整步增大误差时步长减半;收敛判据为末端位置误差 < 1e-8(无量纲)

from e2m2e.algorithm.transfer import StateTerminal, ThreeBodyLambert
from e2m2e.data.templates import ConvergenceState

shooter = ThreeBodyLambert(dynamics)   # system 须已初始化特征尺度

# 终端状态为物理单位 (km, km/s);到达端仅位置为约束,速度用于算到达脉冲
sol = shooter.solve(
    StateTerminal(s0, 0.0),
    StateTerminal(s1, tof),
    tof,                    # 飞行时间,s
    guess="lambert",        # 初猜来源;"orbit" 直接用出发速度
)

if sol.status == ConvergenceState.CONVERGED:
    print(f"出发脉冲: {sol.arcs[0].delta_v:.6f} km/s")
    print(f"到达脉冲: {sol.arrival_delta_v:.6f} km/s")
    print(f"总脉冲: {sol.total_delta_v:.6f} km/s")

返回 TransferSolution,单弧,物理单位; 未收敛时 converged=Falsemessage 说明残余误差。典型场景(同一周期 轨道上两相位点间转移、Lyapunov → Halo 交会)的收敛行为见 tests/algorithm/transfer/test_three_body_lambert.py

多脉冲转移与主矢量检验

双脉冲解只在特定几何下最优。 MultiImpulseTransfer 在固定端点 (StateTerminal,位置、速度、时刻均固定) 之间规划 n 脉冲转移:决策变量为各中途脉冲节点的时刻与位置 [t_i, r_i], 相邻节点间的弧段由 Lambert 封闭(默认二体 solve_lambert,可切 ThreeBodyLambert 打靶精修),脉冲是封闭结果的进出弧速度差,由 scipy SLSQP 最小化总 ΔV。

import numpy as np
from e2m2e.algorithm.transfer import MultiImpulseTransfer, StateTerminal

MU_EARTH = 398600.4418  # km³/s²
R1, R2 = 7000.0, 42164.0  # LEO → GEO,km
TOF_HOHMANN = np.pi * np.sqrt(((R1 + R2) / 2) ** 3 / MU_EARTH)

def circular(r, angle=0.0):
    """半径 r、相位角 angle 的圆轨道状态(逆时针)。"""
    v = np.sqrt(MU_EARTH / r)
    return np.array(
        [r * np.cos(angle), r * np.sin(angle), 0.0,
         -v * np.sin(angle), v * np.cos(angle), 0.0]
    )

transfer = MultiImpulseTransfer(
    StateTerminal(circular(R1), 0.0),
    StateTerminal(circular(R2, np.pi), TOF_HOHMANN),
    mu=MU_EARTH,
)
sol = transfer.optimize(2)
print(f"总脉冲: {sol.total_delta_v:.4f} km/s")   # 霍曼基准 3.7708

optimize(n_impulses, x0=...) 的决策变量只含中途节点(m = n − 2 个), n=2 时无自由变量、直接封闭单弧;x0 给出中途节点的初猜 [t_1, r_1, ...]。优化后 transfer.legs 刷新为 ImpulseCoastArc 交替的序列。

check_primer_vector() 对给定解做 Lawden 主矢量检验:由端点横截条件 p(t0) = Δv̂₀、p(tf) = Δv̂_f 确定主矢量初值,协态经 STM 携载得到 p(t) 曲线 (公式出处为 Prussing《Optimal Spacecraft Trajectories》第 3–4 章)。 最优性的必要条件是全程 |p(t)| 1 且脉冲点 |p| = 1、方向与 p 共线; 弧内 |p| > 1 时在峰值处插入中途脉冲可降低总 ΔV(Lion & Handelsman 1968)。 霍曼转移满足 Lawden 条件;同一端点但飞行时间取 0.5 倍霍曼时间的双脉冲解 弧内 |p| > 1,检验会给出插入建议,以建议点为零脉冲初猜做三脉冲优化, 总 ΔV 随之下降:

report = transfer.check_primer_vector(sol, n_samples=300)
print(f"Lawden 条件满足: {report.lawden_satisfied}")
if not report.lawden_satisfied:
    x0 = np.concatenate(
        [[report.suggested_insertion_time],
         report.suggested_insertion_position]
    )
    sol3 = transfer.optimize(3, x0=x0)   # 三脉冲总 ΔV 低于双脉冲

完整算例见 tests/algorithm/transfer/test_multi_impulse.py

二体 Lambert 求解器(Izzo 算法)的 Python 薄封装。

Rust 内核在 e2m2e-propagation crate(crates/e2m2e-propagation/src/lambert.rs), 经 e2m2e._integrators 暴露;本模块只做类型转换与结果封装。

class e2m2e.algorithm.transfer.lambert.LambertSolution(v0, vf, status, cause, message, n_iter, revs)[源代码]

基类:object

单次 Lambert 求解结果。

变量:
参数:
v0: ndarray
vf: ndarray
status: ConvergenceState
cause: FailureCause
message: str
n_iter: int
revs: int
e2m2e.algorithm.transfer.lambert.solve_lambert(r0, rf, tof, mu, direction='short', revs=0)[源代码]

解二体 Lambert 问题(Izzo 2015 算法)。

参数:
  • r0 (ArrayLike) -- 出发位置 [x, y, z],km

  • rf (ArrayLike) -- 到达位置 [x, y, z],km

  • tof (float) -- 飞行时间,s

  • mu (float) -- 中心天体 GM,km³/s²

  • direction (str) -- "short" 表示转移角 < π,"long" 表示 > π

  • revs (int) -- 完整圈数;≥ 1 时返回右分支(低能)解

返回:

LambertSolution

抛出:

ValueError -- tof 低于该圈数最小转移时间或迭代不收敛

返回类型:

LambertSolution

e2m2e.algorithm.transfer.lambert.solve_lambert_batch(r0_list, rf_list, tof_grid, mu, direction='short', revs=0)[源代码]

N×M 网格批量求解:每个 (r0, rf) 几何对每个 tof 各解一次。

参数:
  • r0_list (ArrayLike) -- 出发位置列表,形状 (N, 3),km

  • rf_list (ArrayLike) -- 到达位置列表,形状 (N, 3),km

  • tof_grid (ArrayLike) -- 飞行时间网格,形状 (M,),s

  • mu (float) -- 中心天体 GM,km³/s²

  • direction (str) -- "short""long"

  • revs (int) -- 完整圈数

返回:

形状 (N, M, 2, 3) 的数组:[..., 0, :] 为出发速度 v0, [..., 1, :] 为到达速度 vf;无解的组合为 NaN。

返回类型:

ndarray

porkchop 图扫描:二体 Lambert 双脉冲 ΔV 网格。

对出发时间 × 飞行时间网格逐点解 Lambert 问题,得到出发/到达脉冲 及其总和的网格数据(porkchop 图的数据层)。终端(出发/到达天体或 轨道)状态经 TerminalCondition 接口提取,本模块不关心状态如何产生。

除内存网格与绘图外,本模块还提供两项任务分析能力(主题 8):

  • SQLite 存档PorkchopData.to_sqlite() / PorkchopData.from_sqlite() 把网格落盘为关系表,多次扫描可累积、可查询(stdlib sqlite3,无新增依赖)。

  • ΔV–TOF Pareto 前沿pareto_front() 用经典非支配排序(Deb 2002) 从网格中提取 Pareto 前沿(Topputo 2013 双目标范式)。

class e2m2e.algorithm.transfer.porkchop.PorkchopData(t_dep, tof, dv1, dv2, total)[源代码]

基类:object

porkchop 扫描结果。

变量:
参数:
t_dep: ndarray
tof: ndarray
dv1: ndarray
dv2: ndarray
total: ndarray
plot(ax=None, levels=None)[源代码]

画总 ΔV 等值线图(porkchop 图)。

参数:
  • ax (Axes | None) -- 目标坐标轴,None 时新建

  • levels (int | npt.ArrayLike | None) -- contour 的等值线层级,None 时取 20

返回:

绘图所用的 Axes

返回类型:

Axes

to_sqlite(path, orbit_pair, *, direction='', revs=0, note='')[源代码]

把本网格写入 SQLite 解数据库,返回本次扫描的 scan_id

库结构两张表:

  • scans(scan_id, orbit_pair, direction, revs, n_t_dep, n_tof, created_at, note):每次调用存一行元数据。

  • design_points(scan_id, t_dep, tof, dv1, dv2, total):网格展平 成 n×m 行,NaN(无解组合)存为 NULL。

同一文件多次调用以自增 scan_id 累积,形成可查询的转移代价 数据库(主题 8)。使用 stdlib sqlite3,不引入新依赖。

参数:
  • path (str | Path) -- SQLite 文件路径;父目录自动创建。

  • orbit_pair (str) -- 轨道对标识,如 "LEO->GEO"

  • direction (str) -- Lambert 转移方向("short"/"long"),记入元数据。

  • revs (int) -- 完整圈数,记入元数据。

  • note (str) -- 自由备注。

返回:

本次扫描的 scan_id

返回类型:

int

classmethod from_sqlite(path, scan_id)[源代码]

scan_id 从 SQLite 解数据库重建网格(NULL 还原为 NaN)。

参数:
返回:

重建的 PorkchopData,与写入时数值等价。

抛出:

ValueError -- scan_id 不存在。

返回类型:

PorkchopData

query(t_dep, tof)[源代码]

双线性插值查询转移代价(总 ΔV)。

在规则网格上定位 (t_dep, tof) 所在的单元格,用四角点双线性 插值估计 total。若四角点含 NaN(无解组合),返回 NaN——调用方 应检查返回值或先用 pareto_front() 筛选有效区域。

对应规划文档「宋亮俊数据库的在线查询」(主题 8):预计算网格 + 双线性插值,替代逐点重算 Lambert。

参数:
  • t_dep (float) -- 出发时间(须在本网格 t_dep 范围内)

  • tof (float) -- 飞行时间(须在本网格 tof 范围内)

返回:

插值得到的总 ΔV(km/s),或 NaN(格点含无解组合)。

抛出:

ValueError -- 查询点超出网格范围。

返回类型:

float

classmethod query_scan(path, scan_id, t_dep, tof)[源代码]

从 SQLite 解数据库读网格并插值查询。

等价于 cls.from_sqlite(path, scan_id).query(t_dep, tof)

参数:
  • path (str | Path) -- SQLite 文件路径。

  • scan_id (int) -- 扫描编号。

  • t_dep (float) -- 查询点(同 query())。

  • tof (float) -- 查询点(同 query())。

返回:

插值得到的总 ΔV(km/s),或 NaN。

返回类型:

float

class e2m2e.algorithm.transfer.porkchop.ParetoFront(t_dep, tof, dv1, dv2, total, rank)[源代码]

基类:object

porkchop 网格的 Pareto 前沿(rank 0 非支配点集)。

变量:
  • t_dep (numpy.ndarray) -- 前沿点的出发时间,形状 (k,)

  • tof (numpy.ndarray) -- 前沿点的飞行时间,形状 (k,)

  • dv1 (numpy.ndarray) -- 前沿点的出发脉冲,形状 (k,),km/s

  • dv2 (numpy.ndarray) -- 前沿点的到达脉冲,形状 (k,),km/s

  • total (numpy.ndarray) -- 前沿点的总脉冲,形状 (k,),km/s

  • rank (numpy.ndarray) -- 各前沿点的非支配层级,形状 (k,),前沿点恒为 0

参数:
t_dep: ndarray
tof: ndarray
dv1: ndarray
dv2: ndarray
total: ndarray
rank: ndarray
plot(ax=None)[源代码]

画 Pareto 前沿(TOF vs 总 ΔV 散点 + 连线)。

参数:

ax (Axes | None) -- 目标坐标轴,None 时新建

返回:

绘图所用的 Axes

返回类型:

Axes

e2m2e.algorithm.transfer.porkchop.pareto_front(data, *, objectives=('total', 'tof'))[源代码]

从 porkchop 网格提取 ΔV–TOF Pareto 前沿(经典非支配排序)。

点 A 支配点 B 当且仅当 A 在两个目标上都不劣于 B、且至少一个目标 严格更优。前沿(rank 0)即不被任何有效点支配的点集。仅对有效点 (total 非 NaN)排序;NaN 组合不参与。

默认目标为 min(total)min(tof) (Topputo 2013 双目标范式)。 也支持 ("dv1", "tof") 等其它字段组合;字段名必须是 PorkchopData 的 ndarray 字段。

复杂度 O(M·N²),N 为有效点数。porkchop 网格通常数百至数千点, 纯 Python 足够;万级以上解集再考虑下沉 Rust。

参数:
  • data (PorkchopData) -- porkchop 扫描结果。

  • objectives (tuple[str, str]) -- 两个目标字段名(越小越优)。

返回:

ParetoFront (仅 rank 0 点)。

抛出:

ValueError -- 目标字段名不存在,或无有效点。

返回类型:

ParetoFront

e2m2e.algorithm.transfer.porkchop.porkchop(dep, arr, t_dep_range, tof_range, mu, dynamics, direction='short', revs=0)[源代码]

扫描出发时间 × 飞行时间网格,计算双脉冲 ΔV。

对网格点 (t_dep, tof):出发终端状态取 t_dep 时刻,到达终端 状态取 t_dep + tof 时刻,解二体 Lambert 得转移速度,脉冲为转移 速度与终端轨道速度之差。

数值网格评估(终端传播 + Lambert + ΔV 组装 + 分发)全部在 Rust (#446,ADR 0017 范式);Python 只做问题构造与结果解释:

  • 规格路径:两端均为内置终端(OrbitTerminal/StateTerminal 且未被 monkeypatch)、涉及轨道终端时 dynamics 为未 patch 的 CR3BP_Dynamics——终端规格直接交给 Rust,轨道终端的传播在 Rust 内逐点并行进行。

  • 协议路径:其余情况(自定义 TerminalCondition 子类、终端 方法被 patch、动力学不满足上条)由 Python 按 get_arrival_state 协议逐点提取状态网格,再交给同一 Rust 评估核。patch 语义因此 保持有效。

两条路径共用同一 Rust Lambert/ΔV 核,无 Python 数值回退;扩展缺失 按 #378 抛 RustExtensionUnavailableError。并行由 Rayon 执行, E2M2E_PORKCHOP_PARALLEL=0 可强制串行(与并行逐位一致)。

参数:
  • dep (TerminalCondition) -- 出发终端(如 OrbitTerminal

  • arr (TerminalCondition) -- 到达终端

  • t_dep_range (ArrayLike) -- 出发时间网格 (n,),与终端时间坐标一致

  • tof_range (ArrayLike) -- 飞行时间网格 (m,),s

  • mu (float) -- 中心天体 GM,km³/s²

  • dynamics (CR3BP_Dynamics) -- 传播对象,透传给终端的 get_arrival_state

  • direction (str) -- "short""long"

  • revs (int) -- 完整圈数

返回:

PorkchopData

返回类型:

PorkchopData

三体 Lambert 打靶模块。

以二体 Lambert 解为初猜,在 CR3BP 动力学下用 Newton 打靶修正出发速度, 使给定飞行时间后的末端位置命中目标。流程(孙俞等 2017、Fossa 等 2022):

  1. 两端物理状态 (km, km/s) 经 CR3BP_System.physical_to_dimensionless 无量纲化;

  2. 无量纲几何上以 μ = 1 调 solve_lambert() 得初猜出发速度;

  3. Newton 迭代:传播 with_stm=True,取末端 STM 的 Φ_rv 块(Φ[0:3, 3:6]) 解修正量,收敛判据为末端位置误差 < 1e-8(无量纲)。

class e2m2e.algorithm.transfer.three_body_lambert.ThreeBodyLambert(dynamics)[源代码]

基类:object

CR3BP 两点边值打靶求解器。

变量:
  • dynamics -- CR3BP_Dynamics 对象(system 须已初始化特征尺度)

  • tolerance -- 末端位置误差收敛判据(无量纲)

  • max_iterations -- 最大 Newton 迭代次数

参数:

dynamics (CR3BP_Dynamics)

DEFAULT_TOLERANCE = 1e-08
DEFAULT_MAX_ITERATIONS = 20
DIVERGENCE_LIMIT = 10.0
__init__(dynamics)[源代码]

初始化

参数:

dynamics (CR3BP_Dynamics) -- CR3BP_Dynamics 对象

抛出:

ValueError -- 系统未初始化特征尺度(物理/无量纲换算不可用)

返回类型:

None

solve(term0, term1, tof, guess='lambert')[源代码]

解三体 Lambert 问题:修正出发速度使 tof 后末端位置命中 term1。

参数:
  • term0 (StateTerminal) -- 出发终端,状态为物理单位 (km, km/s)

  • term1 (StateTerminal) -- 到达终端,状态为物理单位 (km, km/s);仅位置为约束, 速度用于计算到达脉冲

  • tof (float) -- 飞行时间,s

  • guess (Literal['lambert', 'orbit']) -- 初猜来源;"lambert" 用无量纲二体 Lambert 解, "orbit" 直接用 term0 的速度

返回:

TransferSolution,单弧,物理单位;失败时返回对应状态与原因码

返回类型:

TransferSolution

多脉冲转移框架与 Lawden 主矢量检验。

多脉冲 NLP 采用节点参数化:两端为固定 StateTerminal (位置、速度、时刻 均固定),中途脉冲节点以 (t_i, r_i) 为决策变量,相邻节点间的弧段由 Lambert 封闭——二体 solve_lambert() (快,默认,用于初扫)或 ThreeBodyLambert 打靶(准,用于精修)。各节点的脉冲是封闭结果的输出 (进出弧速度差),不是独立变量。这是 Prussing《Optimal Spacecraft Trajectories》 第 5 章与宝音贺西等 (2025) 内层封闭/外层 NLP 结构的标准形式。

主矢量检验的公式出处:

  • Prussing《Optimal Spacecraft Trajectories》(SIAM, 2019) 第 3 章:Lawden 主矢量 p(t) 的最优性必要条件——全程 |p(t)| 1;脉冲点 |p| = 1 且脉冲方向与 p 共线。

  • 同书第 4 章:给定脉冲转移的主矢量由端点横截条件 p(t0) = Δv̂₀、p(tf) = Δv̂_f 确定。协态 z = [−ṗ; p] 沿轨迹按 z(t) = Φ(t, t0)^{-T} z(t0) 携载(Φ 为状态转移 矩阵,固定脉冲不改变变分映射,故 Φ 在脉冲点连续、可跨弧链接);先由 p(tf) 条件 解出 ṗ(t0)(3×3 线性系统),再逐采样点携载得 p(t) 曲线。

  • Lion & Handelsman (1968):|p| 在弧内超过 1 是转移非最优的判据,在峰值处插入 中途脉冲可降低总 ΔV;Grossi et al. (2024) 用它驱动地月三脉冲转移设计。

class e2m2e.algorithm.transfer.multi_impulse.Impulse(time, delta_v)[源代码]

基类:object

脉冲节点:给定时刻的速度增量。

变量:
  • time (float) -- 脉冲时刻,s(与终端时刻同坐标)

  • delta_v (numpy.ndarray) -- 速度增量,形状 (3,),km/s

参数:
time: float
delta_v: ndarray
class e2m2e.algorithm.transfer.multi_impulse.CoastArc(terminal0, tof, dynamics='two_body')[源代码]

基类:object

无动力弧段规划节点:自 terminal0 起飞行 tof。

变量:
参数:
terminal0: StateTerminal
tof: float
dynamics: str = 'two_body'
class e2m2e.algorithm.transfer.multi_impulse.PrimerVectorReport(times, primer, primer_magnitude, max_magnitude, lawden_satisfied, impulse_times, impulse_magnitudes, impulse_alignment_cosines, suggested_insertion_time, suggested_insertion_position, message='')[源代码]

基类:object

主矢量检验报告。

变量:
  • times (numpy.ndarray) -- 采样时刻,形状 (n,),s(与终端时刻同坐标)

  • primer (numpy.ndarray) -- 主矢量 p(t),形状 (n, 3),无量纲

  • primer_magnitude (numpy.ndarray) -- |p(t)|,形状 (n,)

  • max_magnitude (float) -- 全程最大 |p|

  • lawden_satisfied (bool) -- Lawden 必要条件是否满足(弧内 |p|≤1 且中途脉冲 与 p 共线、|p|=1,均在容差内)

  • impulse_times (numpy.ndarray) -- 各脉冲时刻(含出发与到达脉冲),形状 (k,),s

  • impulse_magnitudes (numpy.ndarray) -- 脉冲点 |p|,形状 (k,)

  • impulse_alignment_cosines (numpy.ndarray) -- 脉冲方向与 p 的夹角余弦,形状 (k,); 零脉冲方向无定义,记为 NaN

  • suggested_insertion_time (float | None) -- 建议的中途脉冲插入时刻(弧内 |p|>1 的峰值处), 无违例时为 None

  • suggested_insertion_position (numpy.ndarray | None) -- 建议插入点位置,形状 (3,),km

  • message (str) -- 附加说明(端点携载误差、违例细节等)

参数:
times: ndarray
primer: ndarray
primer_magnitude: ndarray
max_magnitude: float
lawden_satisfied: bool
impulse_times: ndarray
impulse_magnitudes: ndarray
impulse_alignment_cosines: ndarray
suggested_insertion_time: float | None
suggested_insertion_position: ndarray | None
message: str = ''
class e2m2e.algorithm.transfer.multi_impulse.MultiImpulseTransfer(term0, term1, *, mu=None, dynamics=None)[源代码]

基类:object

固定端点间的多脉冲转移规划器。

变量:
参数:
closure: Literal['two_body', 'three_body']
legs: list[Impulse | CoastArc]
optimize(n_impulses, bounds=None, *, backend='scipy', closure=None, x0=None, min_dt=0.001, verbose=False)[源代码]

优化 n_impulses 脉冲转移,最小化总 ΔV。

决策变量为各中途脉冲节点的时刻与位置 [t_1..t_m, r_1..r_m] (m = n_impulses − 2;时刻为相对 term0.time 的秒数,位置为 km), 弧段按 closure 封闭,脉冲为封闭速度差。n_impulses=2 时无自由变量, 直接封闭单弧返回。

参数:
  • n_impulses (int) -- 脉冲总数(含出发与到达脉冲),≥ 2

  • bounds (dict[str, tuple] | None) -- 中途节点界限 {"t": (lo, hi), "r": (lo3, hi3)}"t" 为相对 term0.time 的秒数区间,"r" 为位置分量盒 (km),对所有中途节点统一适用;None 时用默认界限

  • backend (str) -- NLP 后端,当前仅支持 "scipy" (SLSQP)

  • closure (Literal['two_body', 'three_body'] | None) -- 弧段封闭方式;None 时取实例默认

  • x0 (ArrayLike | None) -- 初猜决策向量;None 时中途节点在时间上均布、位置线性插值

  • min_dt (float) -- 相邻脉冲最小间隔,s

  • verbose (bool) -- 是否打印 SLSQP 迭代信息

返回:

TransferSolution,弧段数 = n_impulses − 1

返回类型:

TransferSolution

备注

SLSQP 对零脉冲初猜(目标值恰为双脉冲成本的弧上点)数值敏感:目标函数 在该平坦走廊上梯度极小,收敛判据可能提前触发、一步即停(#384)。首次 优化相对初猜改善不足时,自动从微扰初猜重试,取总 ΔV 最小者。

check_primer_vector(solution, n_samples=100, tol=0.001, closure=None)[源代码]

对给定转移解做主矢量检验(Lawden 必要条件)。

端点横截条件 p(t0) = Δv̂₀、p(tf) = Δv̂_f,协态 z = [−ṗ; p] 经 z(t) = Φ(t, t0)^{-T} z(t0) 携载(公式出处见模块 docstring)。

参数:
  • solution (TransferSolution) -- 待检验的转移解(弧段初态须为脉冲后状态)

  • n_samples (int) -- 每弧采样点数

  • tol (float) -- Lawden 判据容差(|p|≤1+tol、脉冲点 |p|∈1±tol、 共线 |cos|≥1−tol

  • closure (Literal['two_body', 'three_body'] | None) -- 弧段重传播所用动力学;None 时取实例默认,须与产生 solution 时的封闭方式一致

返回:

PrimerVectorReport

抛出:

ValueError -- 出发或到达脉冲过小,无法确定主矢量端点方向

返回类型:

PrimerVectorReport