11 KiB
天体轨道生成系统文档
版本: 1.0 最后更新: 2025-12-10
概述
Cosmo项目中的行星和矮行星轨道采用预计算方式存储在数据库中,而不是实时计算。这样做的好处是:
- 性能优化 - 前端无需实时计算复杂的椭圆轨道
- NASA数据 - 使用真实的NASA JPL Horizons API数据
- 精确性 - 考虑了引力摄动等真实天文因素
轨道生成逻辑
1. 适用天体类型
轨道生成功能仅适用于以下两种天体类型:
- 行星 (planet) - 八大行星
- 矮行星 (dwarf_planet) - 冥王星、谷神星、阋神星等
2. 生成流程
步骤1: 确定轨道参数
在 backend/app/api/celestial_orbit.py 中定义了硬编码的轨道周期:
ORBITAL_PERIODS = {
# 行星 - 一个完整公转周期
"199": 88.0, # 水星 (88天)
"299": 224.7, # 金星
"399": 365.25, # 地球 (1年)
"499": 687.0, # 火星
"599": 4333.0, # 木星 (11.86年)
"699": 10759.0, # 土星 (29.46年)
"799": 30687.0, # 天王星 (84.01年)
"899": 60190.0, # 海王星 (164.79年)
# 矮行星 - 一个完整公转周期
"999": 90560.0, # 冥王星 (247.94年)
"2000001": 1680.0, # 谷神星 (4.6年)
"136199": 203500.0,# 阋神星 (557年)
"136108": 104000.0,# 妊神星 (285年)
"136472": 112897.0,# 鸟神星 (309年)
}
轨道颜色也是硬编码的:
DEFAULT_COLORS = {
"199": "#8C7853", # 水星 - 棕色
"299": "#FFC649", # 金星 - 黄色
"399": "#4A90E2", # 地球 - 蓝色
"499": "#CD5C5C", # 火星 - 红色
# ... 其他天体
}
步骤2: 计算采样点数量
采样策略(orbit_service.py):
MIN_POINTS = 100 # 最少100个点,保证椭圆光滑
MAX_POINTS = 1000 # 最多1000个点,避免数据过大
if period_days < 3650: # < 10年
# 行星:约每天1个点,最少100个
num_points = max(MIN_POINTS, min(int(period_days), 365))
else: # >= 10年
# 外行星和矮行星:每月采样一次
num_points = min(int(period_days / 30), MAX_POINTS)
示例:
- 地球 (365.25天) → 365个采样点
- 冥王星 (90560天 ≈ 248年) → 1000个采样点(每90天一个)
步骤3: 查询NASA Horizons API
调用NASA JPL Horizons API获取真实轨道数据:
positions = await horizons_service.get_body_positions(
body_id=body_id,
start_time=start_time,
end_time=end_time,
step=f"{step_days}d"
)
特殊处理:
- 短周期天体(<150年):从当前时间开始
- 长周期天体(≥150年):从1900年开始(避免超出NASA数据范围)
步骤4: 存储到数据库
将轨道点存储到 orbits 表:
CREATE TABLE orbits (
body_id VARCHAR(50) PRIMARY KEY,
points JSONB NOT NULL, -- [{"x": 1.0, "y": 0.5, "z": 0.0}, ...]
num_points INTEGER, -- 点的数量
period_days DOUBLE PRECISION, -- 轨道周期(天)
color VARCHAR(20), -- 轨道线颜色
created_at TIMESTAMP,
updated_at TIMESTAMP
);
当前问题与解决方案
问题1: 新增天体后轨道不自动生成
现状:管理员在天体管理界面新增矮行星后,需要手动执行以下步骤:
- 到"NASA数据下载"页面
- 点击"生成轨道"按钮
- 系统遍历所有行星/矮行星,调用NASA API生成轨道
问题:
- 流程繁琐,容易遗忘
- 无法针对单个天体生成
- 新增天体时轨道参数(周期、颜色)是硬编码的
问题2: 轨道参数硬编码
现状:轨道周期和颜色定义在 celestial_orbit.py 中,无法灵活配置。
问题:
- 新增矮行星必须修改代码添加周期和颜色
- 无法为自定义天体生成轨道
- 缺乏数据库层面的配置灵活性
解决方案设计
方案A: 自动触发轨道生成(推荐)
实现思路:
-
数据库扩展 - 在
celestial_bodies表添加轨道参数字段:ALTER TABLE celestial_bodies ADD COLUMN orbit_period_days DOUBLE PRECISION; ALTER TABLE celestial_bodies ADD COLUMN orbit_color VARCHAR(20); ALTER TABLE celestial_bodies ADD COLUMN auto_generate_orbit BOOLEAN DEFAULT FALSE; -
创建天体时自动生成 - 修改
POST /celestialAPI:@router.post("") async def create_celestial_body(body_data, db): # 1. 创建天体 new_body = await celestial_body_service.create_body(body_data.dict(), db) # 2. 如果是行星/矮行星且设置了轨道参数,自动生成轨道 if new_body.type in ["planet", "dwarf_planet"] and new_body.orbit_period_days: await orbit_service.generate_orbit( body_id=new_body.id, body_name=new_body.name_zh or new_body.name, period_days=new_body.orbit_period_days, color=new_body.orbit_color or "#CCCCCC", session=db, horizons_service=horizons_service ) -
前端界面调整 - 在天体新增表单中添加:
- 轨道周期输入框(天)
- 轨道颜色选择器
- "自动生成轨道"复选框
优点:
- ✅ 无需手动操作,完全自动化
- ✅ 轨道参数可配置
- ✅ 新增天体立即可用
缺点:
- ⚠️ 创建天体时可能耗时较长(等待NASA API响应)
- ⚠️ 需要修改数据库结构
方案B: 异步后台生成
实现思路:
- 创建天体时立即返回,在后台异步生成轨道
- 使用Celery或FastAPI BackgroundTasks
- 前端显示"轨道生成中..."状态
优点:
- ✅ 用户体验好,不会阻塞
- ✅ 可以批量生成
缺点:
- ⚠️ 需要引入任务队列(增加系统复杂度)
- ⚠️ 需要轮询检查生成状态
方案C: 手动触发但优化流程(最简单)
实现思路:
- 在天体列表页添加"生成轨道"按钮(每行一个)
- 点击后调用
POST /celestial/admin/orbits/generate?body_ids={id} - 使用天体的
extra_data字段存储轨道参数
优点:
- ✅ 实现简单,无需修改数据库
- ✅ 灵活可控
缺点:
- ⚠️ 仍需手动操作
推荐实施步骤
Phase 1: 快速修复(方案C)
-
修改
CelestialBodyCreate模型,允许在extra_data中传入:{ "orbit_period_days": 90560.0, "orbit_color": "#8B7355" } -
修改轨道生成API,优先从
extra_data读取参数:# 优先从天体的extra_data读取,其次从硬编码字典读取 extra_data = body.extra_data or {} period = extra_data.get("orbit_period_days") or ORBITAL_PERIODS.get(body.id) color = extra_data.get("orbit_color") or DEFAULT_COLORS.get(body.id, "#CCCCCC") -
前端添加按钮 - 在天体管理列表每行添加"生成轨道"操作按钮
Phase 2: 自动化(方案A)
- 添加数据库迁移,新增轨道参数字段
- 修改创建天体API,支持自动生成
- 前端表单添加轨道参数输入
API接口文档
生成轨道
端点: POST /celestial/admin/orbits/generate
查询参数:
body_ids(可选) - 逗号分隔的天体ID列表,如 "999,2000001"- 如果不提供,则为所有行星和矮行星生成轨道
响应示例:
{
"message": "Generated 2 orbits (0 failed)",
"results": [
{
"body_id": "999",
"body_name": "冥王星",
"status": "success",
"num_points": 1000,
"period_days": 90560.0
}
]
}
获取轨道数据
端点: GET /celestial/orbits
查询参数:
body_type(可选) - 过滤天体类型,如 "planet" 或 "dwarf_planet"
响应示例:
{
"orbits": [
{
"body_id": "399",
"body_name": "地球",
"body_name_zh": "地球",
"points": [
{"x": 1.0, "y": 0.0, "z": 0.0},
{"x": 0.99, "y": 0.01, "z": 0.0},
...
],
"num_points": 365,
"period_days": 365.25,
"color": "#4A90E2",
"updated_at": "2025-12-10T10:30:00"
}
]
}
删除轨道
端点: DELETE /celestial/admin/orbits/{body_id}
响应:
{
"message": "Orbit for 999 deleted successfully"
}
数据库结构
orbits 表
| 字段 | 类型 | 说明 |
|---|---|---|
| body_id | VARCHAR(50) | 天体ID(主键,外键到celestial_bodies) |
| points | JSONB | 轨道点数组 [{"x", "y", "z"}, ...] |
| num_points | INTEGER | 轨道点数量 |
| period_days | DOUBLE PRECISION | 轨道周期(天) |
| color | VARCHAR(20) | 轨道线颜色(HEX) |
| created_at | TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | 更新时间 |
索引:
- PRIMARY KEY:
body_id - FOREIGN KEY:
body_id→celestial_bodies.id(ON DELETE CASCADE)
前端使用
获取并渲染轨道
// 1. 获取轨道数据
const response = await request.get('/celestial/orbits?body_type=planet');
const orbits = response.data.orbits;
// 2. 渲染轨道线
orbits.forEach(orbit => {
const points = orbit.points.map(p => new Vector3(p.x, p.y, p.z));
const geometry = new BufferGeometry().setFromPoints(points);
const material = new LineBasicMaterial({ color: orbit.color });
const line = new Line(geometry, material);
scene.add(line);
});
常见问题
Q1: 为什么不实时计算轨道?
A: 实时计算需要考虑多体引力、引力摄动等复杂因素,计算量大且不准确。预计算方式使用NASA真实数据,更准确且性能更好。
Q2: 如何为新增的矮行星生成轨道?
A:
- 短期方案:在天体的
extra_data中添加orbit_period_days和orbit_color - 长期方案:等待数据库迁移,使用独立字段存储轨道参数
Q3: NASA Horizons API有哪些限制?
A:
- 时间范围:通常限制在1900-2200年之间
- 频率限制:建议每次查询间隔1秒
- 天体覆盖:只包含太阳系天体,不包括系外行星
Q4: 轨道数据多久更新一次?
A: 理论上轨道是稳定的,无需频繁更新。建议:
- 行星:每年更新一次(考虑引力摄动)
- 矮行星:每5年更新一次
未来优化方向
- 自动化轨道生成 - 创建天体时自动触发轨道生成
- 轨道参数配置化 - 从数据库读取而非硬编码
- 批量生成优化 - 并发调用NASA API,提升生成速度
- 轨道缓存策略 - 避免重复生成相同天体的轨道
- 支持更多天体 - 扩展到卫星、小行星等
文档作者: Claude Code AI 版本历史:
- v1.0 (2025-12-10) - 初始版本,基于现有代码分析