轨道保持示例(main_control.py)#
设计 Halo 标称轨道后做轨道保持蒙特卡洛仿真,绘制标称与受控轨迹对比 (会合系 x-z 投影,含五个平动点标注)。样本量刻意压小(2 个控制周期、 1 个蒙特卡洛样本),是快速演示版;工程评估把样本提到惯例值 100。
前置:SPICE 内核在仓库根 kernels/。
uv run --no-sync python examples/main_control.py --save
#!/usr/bin/env python3
"""main_control —— 轨道保持示例
先用 ``design_orbit`` 设计一条 Halo 标称轨道,再用 ``control_orbit``
做轨道保持蒙特卡洛仿真,绘制受控轨迹与标称轨迹对比。
用法:
python examples/main_control.py # 交互式出图
python examples/main_control.py --save # 存成 PNG(无头服务器可用)
前置条件:SPICE 内核位于仓库根 ``kernels/``(或设 ``$SPICE_KERNEL_DIR``)。
"""
from __future__ import annotations
import argparse
import pathlib
import numpy as np
# 输出图片保存到脚本所在目录(无论从哪运行)
_OUT_DIR = pathlib.Path(__file__).resolve().parent
def main() -> None:
parser = argparse.ArgumentParser(description="轨道保持示例(Halo 蒙特卡洛)")
parser.add_argument("--save", action="store_true", help="存为 PNG 而非交互式显示")
parser.add_argument(
"--log-level", default="WARNING", help="日志级别(DEBUG/INFO/WARNING/ERROR)"
)
args = parser.parse_args()
from e2m2e.tools.logging import configure_logging
configure_logging(level=args.log_level)
print("=" * 60)
print("e2m2e 轨道保持示例(Halo)")
print("=" * 60)
if args.save:
import matplotlib
matplotlib.use("Agg")
from _plot_setup import setup_cjk_font
setup_cjk_font()
from e2m2e.algorithm.design import design_orbit
from e2m2e.algorithm.station_keeping import control_orbit
from e2m2e.api.models import DesignOrbitRequest
# 1. 设计一条短弧 Halo 标称轨道(供轨道保持)
print("\n1. 设计 L2 Halo 标称轨道")
# 摄动开关:太阳第三体引力 + 地月非球形(10 阶)+ 炮弹模型光压
perturbation = {
"sun_body": 1,
"planets": 0,
"earth_nonspherical": 1,
"moon_nonspherical": 1,
"solar_radiation": 1,
"atmosphere": 0,
"relativity": 0,
"tide": 0,
"coupling": 0,
}
print(" 摄动开关:")
for k, v in perturbation.items():
print(f" {k} = {v}")
result = design_orbit(
DesignOrbitRequest(
orbit_type="HALO",
collinear_point=2,
amplitude=30000.0,
phase=0.0,
duration=0.1 * 365.25 * 86400,
output_step=3600.0,
perturbation=perturbation,
)
)
print(f" 星历行数 = {len(result.ephemeris)}")
# 2. 轨道保持蒙特卡洛仿真(少量控制/样本,快速演示)
print("\n2. 轨道保持蒙特卡洛仿真")
print(" 控制模式 1(目标点宽松),2 个控制周期,1 个蒙特卡洛样本")
ctl = control_orbit(
result.ephemeris,
control_mode=1,
num_controls=2,
num_monte_carlo=1,
control_interval=10.0,
output_step=3600.0,
perturbation=perturbation,
)
print(f" 失败样本数 = {ctl.num_failed}")
rows = np.asarray(ctl.sk_statistic.rows)
if rows.size:
print(f" 总 Δv = {rows[0, 0]:.3f} m/s,最大 Δv = {rows[0, 1]:.3f} m/s")
else:
print(" 统计为空(无有效样本)")
# 3. 绘制标称 vs 受控轨迹(会合系 x-z)
print("\n3. 绘制标称与受控轨迹对比")
from e2m2e.algorithm.family.cr3bp_orbits import earth_moon_system
system = earth_moon_system()
# 标称:会合系无量纲状态
nominal = result.ephemeris.synodic_position
# 直接用 matplotlib 绘 x-z 投影:标称 vs 受控 + 地月天体 + 平动点
import matplotlib.pyplot as plt
from e2m2e.algorithm.dynamics import LibrationPoint
fig, ax1 = plt.subplots(figsize=(12, 10), dpi=100)
ax1.plot(
nominal[:, 0],
nominal[:, 2],
label="标称轨道",
linewidth=1.5,
alpha=0.8,
)
# 受控:最后一次样本的受控星历(若可用)
if ctl.controlled_ephemeris is not None:
controlled = ctl.controlled_ephemeris.synodic_position
ax1.plot(
controlled[:, 0],
controlled[:, 2],
color="orange",
label="受控轨道",
linewidth=1.5,
alpha=0.8,
)
# 天体标记(质心归一坐标:主天体在 -mu,次天体在 1-mu)
mu = system.mu
ax1.scatter(
-mu,
0,
color="#2E86AB",
s=200,
edgecolors="#1A5276",
linewidth=1.5,
zorder=10,
label="Earth",
)
ax1.scatter(
1 - mu,
0,
color="#95A5A6",
s=100,
edgecolors="#566573",
linewidth=1.5,
zorder=10,
label="Moon",
)
# 五个平动点(灰色三角 + 标签)
for i, lp in enumerate(LibrationPoint):
coord = system.L_points[lp]
ax1.scatter(coord[0], coord[1], color="gray", marker="^", s=60, zorder=5)
ax1.annotate(
f"L{i + 1}",
(coord[0], coord[1]),
textcoords="offset points",
xytext=(5, 5),
fontsize=16,
)
ax1.set_xlabel("X(无量纲)")
ax1.set_ylabel("Z(无量纲)")
ax1.set_title("L2 Halo 轨道保持:标称 vs 受控(会合系 x-z)")
ax1.legend(loc="upper right")
if args.save:
fig.savefig(
str(_OUT_DIR / "main_control_halo.png"),
dpi=150,
bbox_inches="tight",
pad_inches=0.1,
)
print(f" 已保存 {_OUT_DIR / 'main_control_halo.png'}")
else:
plt.show()
print("\n" + "=" * 60)
print("示例完成!")
print("=" * 60)
if __name__ == "__main__":
main()
产出与解读#
图:
examples/main_control_halo.png——标称轨道与受控轨道的 x-z 投影 对比,地月天体与 L1–L5 标注同坐标系。结果字段:
ctl.num_failed是蒙特卡洛失败样本数;ctl.sk_statistic.rows首行前两列是总 Δv 与最大单次 Δv(m/s);ctl.controlled_ephemeris.synodic_position是受控星历(全部样本失败 时为None),画图前判空。control_orbit(result.ephemeris, control_mode=1, num_controls=2, num_monte_carlo=1, control_interval=10.0, ...):控制模式 1 = 目标点 宽松;控制力模型与真实力模型的摄动开关可分别指定。
对照:接口层等价调用见 轨道保持。