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 打靶修正出发速度,使给定飞行时间后的末端
位置命中目标:
两端物理状态 (km, km/s) 经
CR3BP_System.physical_to_dimensionless无量纲化无量纲几何上以 μ = 1 调
solve_lambert得初猜出发速度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=False 且 message 说明残余误差。典型场景(同一周期
轨道上两相位点间转移、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 刷新为
Impulse 与
CoastArc 交替的序列。
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 (numpy.ndarray) -- 出发速度,形状
(3,),km/svf (numpy.ndarray) -- 到达速度,形状
(3,),km/sstatus (e2m2e.data.templates.enums.ConvergenceState) -- 算法最终状态(Rust 后端失败时直接抛错)
cause (e2m2e.data.templates.enums.FailureCause) -- 算法最终原因码
message (str) -- 求解器结果说明
n_iter (int) -- Householder 迭代次数
revs (int) -- 完整圈数
- 参数:
v0 (ndarray)
vf (ndarray)
status (ConvergenceState)
cause (FailureCause)
message (str)
n_iter (int)
revs (int)
- 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 算法)。
- 参数:
- 返回:
- 抛出:
ValueError -- tof 低于该圈数最小转移时间或迭代不收敛
- 返回类型:
- e2m2e.algorithm.transfer.lambert.solve_lambert_batch(r0_list, rf_list, tof_grid, mu, direction='short', revs=0)[源代码]
N×M 网格批量求解:每个 (r0, rf) 几何对每个 tof 各解一次。
- 参数:
- 返回:
形状
(N, M, 2, 3)的数组:[..., 0, :]为出发速度 v0,[..., 1, :]为到达速度 vf;无解的组合为 NaN。- 返回类型:
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)[源代码]
基类:
objectporkchop 扫描结果。
- 变量:
t_dep (numpy.ndarray) -- 出发时间网格,形状
(n,)tof (numpy.ndarray) -- 飞行时间网格,形状
(m,)dv1 (numpy.ndarray) -- 出发脉冲大小,形状
(n, m),km/sdv2 (numpy.ndarray) -- 到达脉冲大小,形状
(n, m),km/stotal (numpy.ndarray) -- 总脉冲
dv1 + dv2,形状(n, m),km/s
- 参数:
- t_dep: ndarray
- tof: ndarray
- dv1: ndarray
- dv2: ndarray
- total: ndarray
- plot(ax=None, levels=None)[源代码]
画总 ΔV 等值线图(porkchop 图)。
- 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)。使用 stdlibsqlite3,不引入新依赖。
- classmethod from_sqlite(path, scan_id)[源代码]
按
scan_id从 SQLite 解数据库重建网格(NULL 还原为 NaN)。- 参数:
scan_id (int) --
to_sqlite()返回的扫描编号。
- 返回:
重建的
PorkchopData,与写入时数值等价。- 抛出:
ValueError --
scan_id不存在。- 返回类型:
- query(t_dep, tof)[源代码]
双线性插值查询转移代价(总 ΔV)。
在规则网格上定位
(t_dep, tof)所在的单元格,用四角点双线性 插值估计total。若四角点含 NaN(无解组合),返回 NaN——调用方 应检查返回值或先用pareto_front()筛选有效区域。对应规划文档「宋亮俊数据库的在线查询」(主题 8):预计算网格 + 双线性插值,替代逐点重算 Lambert。
- 参数:
- 返回:
插值得到的总 ΔV(km/s),或 NaN(格点含无解组合)。
- 抛出:
ValueError -- 查询点超出网格范围。
- 返回类型:
- class e2m2e.algorithm.transfer.porkchop.ParetoFront(t_dep, tof, dv1, dv2, total, rank)[源代码]
基类:
objectporkchop 网格的 Pareto 前沿(rank 0 非支配点集)。
- 变量:
t_dep (numpy.ndarray) -- 前沿点的出发时间,形状
(k,)tof (numpy.ndarray) -- 前沿点的飞行时间,形状
(k,)dv1 (numpy.ndarray) -- 前沿点的出发脉冲,形状
(k,),km/sdv2 (numpy.ndarray) -- 前沿点的到达脉冲,形状
(k,),km/stotal (numpy.ndarray) -- 前沿点的总脉冲,形状
(k,),km/srank (numpy.ndarray) -- 各前沿点的非支配层级,形状
(k,),前沿点恒为 0
- 参数:
- t_dep: ndarray
- tof: ndarray
- dv1: ndarray
- dv2: ndarray
- total: ndarray
- rank: ndarray
- 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 扫描结果。
- 返回:
ParetoFront(仅 rank 0 点)。- 抛出:
ValueError -- 目标字段名不存在,或无有效点。
- 返回类型:
- 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,),smu (float) -- 中心天体 GM,km³/s²
dynamics (CR3BP_Dynamics) -- 传播对象,透传给终端的
get_arrival_statedirection (str) --
"short"或"long"revs (int) -- 完整圈数
- 返回:
- 返回类型:
三体 Lambert 打靶模块。
以二体 Lambert 解为初猜,在 CR3BP 动力学下用 Newton 打靶修正出发速度, 使给定飞行时间后的末端位置命中目标。流程(孙俞等 2017、Fossa 等 2022):
两端物理状态 (km, km/s) 经
CR3BP_System.physical_to_dimensionless无量纲化;无量纲几何上以 μ = 1 调
solve_lambert()得初猜出发速度;Newton 迭代:传播
with_stm=True,取末端 STM 的 Φ_rv 块(Φ[0:3, 3:6]) 解修正量,收敛判据为末端位置误差 < 1e-8(无量纲)。
- class e2m2e.algorithm.transfer.three_body_lambert.ThreeBodyLambert(dynamics)[源代码]
基类:
objectCR3BP 两点边值打靶求解器。
- 变量:
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,单弧,物理单位;失败时返回对应状态与原因码- 返回类型:
多脉冲转移框架与 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 (e2m2e.algorithm.transfer.terminal.StateTerminal) -- 弧段起点终端(状态为脉冲后的出发状态)
tof (float) -- 弧段飞行时间,s
dynamics (str) -- 弧段封闭动力学标识,
"two_body"或"three_body"
- 参数:
terminal0 (StateTerminal)
tof (float)
dynamics (str)
- 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,),simpulse_magnitudes (numpy.ndarray) -- 脉冲点
|p|,形状(k,)impulse_alignment_cosines (numpy.ndarray) -- 脉冲方向与 p 的夹角余弦,形状
(k,); 零脉冲方向无定义,记为 NaNsuggested_insertion_time (float | None) -- 建议的中途脉冲插入时刻(弧内
|p|>1的峰值处), 无违例时为 Nonesuggested_insertion_position (numpy.ndarray | None) -- 建议插入点位置,形状
(3,),kmmessage (str) -- 附加说明(端点携载误差、违例细节等)
- 参数:
- times: ndarray
- primer: ndarray
- primer_magnitude: ndarray
- max_magnitude: float
- lawden_satisfied: bool
- impulse_times: ndarray
- impulse_magnitudes: ndarray
- impulse_alignment_cosines: ndarray
- message: str = ''
- class e2m2e.algorithm.transfer.multi_impulse.MultiImpulseTransfer(term0, term1, *, mu=None, dynamics=None)[源代码]
基类:
object固定端点间的多脉冲转移规划器。
- 变量:
term0 -- 出发终端(位置、速度、时刻固定)
term1 -- 到达终端(位置、速度、时刻固定)
mu -- 二体中心天体 GM,km³/s²(二体封闭必填)
dynamics -- CR3BP_Dynamics(三体封闭必填)
closure (Literal['two_body', 'three_body']) -- 默认弧段封闭方式
legs (list[e2m2e.algorithm.transfer.multi_impulse.Impulse | e2m2e.algorithm.transfer.multi_impulse.CoastArc]) -- 当前方案的脉冲/弧段序列;初始为单弧,
optimize()后刷新
- 参数:
term0 (StateTerminal)
term1 (StateTerminal)
mu (float | None)
dynamics (CR3BP_Dynamics | None)
- closure: Literal['two_body', 'three_body']
- 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- 返回类型:
备注
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 时的封闭方式一致
- 返回:
- 抛出:
ValueError -- 出发或到达脉冲过小,无法确定主矢量端点方向
- 返回类型: