卫星3D可视化项目推进总结(面向航天爱好者的实践与优化)
作为一名航天爱好者,我一直希望能打造一个直观、易用的卫星3D可视化平台,让更多同好能够轻松观察卫星在轨运动轨迹、了解卫星核心信息,摆脱传统卫星数据枯燥、抽象的呈现方式。本次项目推进围绕"服务航天爱好者"这一核心目标,逐步完成了需求拆解、接口优化、工作流规划等关键环节,每一步操作都紧扣航天爱好者的使用痛点。本文将结合具体操作、示例代码和分层解析,详细总结项目推进全流程,为同类项目开发提供参考。
一、项目核心定位与需求拆解(明确方向,贴合航天爱好者需求)
项目启动初期,首要任务是明确核心定位------面向航天爱好者的卫星可视化工具,而非专业航天工程工具。这一定位直接决定了项目的核心需求,也为后续所有开发工作划定了方向。
1.1 核心需求拆解(贴合航天爱好者痛点)
航天爱好者群体的核心诉求是"看得懂、看得清、能了解",结合这一特点,拆解出三大核心需求:
-
直观性:将复杂的TLE轨道数据转化为3D可视化场景,避免抽象数据带来的理解门槛;
-
易用性:操作简单,支持按国家、轨道类型筛选卫星,满足针对性查看需求;
-
信息完整性:不仅能看轨迹,还能快速获取卫星所属国家、用途、轨道特点等核心信息。
之所以聚焦这三大需求,是因为传统卫星可视化工具普遍存在"重轨迹、轻信息"的问题,导致爱好者只能"看个热闹",无法深入了解卫星价值,这与"传递航天知识、服务航天爱好者"的核心目标相悖。
1.2 项目技术栈选型(适配需求,兼顾易用性与可扩展性)
结合需求,选用以下技术栈,确保项目稳定、高效且易于维护:
-
前端:Three.js(构建3D地球场景、卫星渲染与轨道动画)、原生JS(交互逻辑、接口绑定);
-
后端/脚本:Python(TLE数据爬取、解析,AI接口调用,JSON生成);
-
数据源:CelesTrak官方TLE数据(权威、全面,保障数据准确性);
-
AI工具:豆包API(自动生成卫星简介,降低手动工作量)。
二、项目基础搭建(实现核心可视化功能,满足基础查看需求)
基础搭建是项目的核心支撑,重点实现3D场景渲染、卫星轨迹模拟和基础筛选功能,为后续优化奠定基础。本章节将结合关键示例代码,详细说明搭建过程。
2.1 3D地球场景与星空背景搭建(Three.js核心实现)
通过Three.js构建3D场景,加载地球纹理、模拟地球自转,打造贴近真实太空的可视化效果,贴合航天爱好者的视觉需求。
核心示例代码(场景、相机、地球渲染):
javascript
// 1. 创建场景、相机、渲染器
function initScene() {
// 场景创建(黑色背景模拟太空)
scene = new THREE.Scene();
scene.background = new THREE.Color(0x050510);
// 相机设置(适配窗口比例,保证视角舒适)
const aspect = window.innerWidth / window.innerHeight;
camera = new THREE.PerspectiveCamera(
60, // 视野角度
aspect,
0.1, // 近裁剪面
2000 // 远裁剪面
);
camera.position.set(300, 150, 300); // 初始相机位置
// 渲染器设置(抗锯齿,提升视觉效果)
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.getElementById('canvas-container').appendChild(renderer.domElement);
// 控制器(轨道控制,支持拖拽、缩放)
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 150;
controls.maxDistance = 800;
}
// 2. 地球渲染(加载纹理,添加大气层效果)
function createEarth() {
earth = new THREE.Group();
scene.add(earth);
// 地球几何体(球体,细分度64,保证圆润)
const geometry = new THREE.SphereGeometry(100, 64, 64);
// 加载地球纹理(蓝色大理石纹理,贴合真实地球外观)
const textureLoader = new THREE.TextureLoader();
const earthTexture = textureLoader.load('https://unpkg.com/three-globe/example/img/earth-blue-marble.jpg');
const bumpTexture = textureLoader.load('https://unpkg.com/three-globe/example/img/earth-topology.png');
// 地球材质(添加凹凸纹理,提升立体感)
const material = new THREE.MeshPhongMaterial({
map: earthTexture,
bumpMap: bumpTexture,
bumpScale: 2,
specular: new THREE.Color(0x333333),
shininess: 5
});
// 地球网格对象
earthMesh = new THREE.Mesh(geometry, material);
earth.add(earthMesh);
// 大气层效果(提升视觉层次感)
const atmosphereGeometry = new THREE.SphereGeometry(105, 64, 64);
const atmosphereMaterial = new THREE.MeshPhongMaterial({
color: 0x4fc3f7,
transparent: true,
opacity: 0.15,
side: THREE.BackSide
});
const atmosphere = new THREE.Mesh(atmosphereGeometry, atmosphereMaterial);
earth.add(atmosphere);
}
// 3. 星空背景(模拟太空环境,提升沉浸感)
function createStarField() {
const starGeometry = new THREE.BufferGeometry();
const starCount = 3000;
const positions = new Float32Array(starCount * 3);
// 随机生成星空位置
for (let i = 0; i < starCount * 3; i += 3) {
const radius = 1000 + Math.random() * 2000;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
positions[i] = radius * Math.sin(phi) * Math.cos(theta);
positions[i + 1] = radius * Math.sin(phi) * Math.sin(theta);
positions[i + 2] = radius * Math.cos(phi);
}
starGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const starMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 2,
transparent: true,
opacity: 0.8
});
const stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);
}
代码说明:以上代码完成了3D场景的核心搭建,包括场景、相机、渲染器的初始化,地球纹理加载、大气层效果和星空背景的实现,确保航天爱好者能看到直观、逼真的太空场景。
2.2 卫星轨迹渲染与基础交互实现
通过卫星管理器实现卫星批量渲染和轨道更新,同时添加鼠标交互(悬停显示基础信息、点击弹窗),满足爱好者"查看卫星、了解基础信息"的需求。
核心示例代码(卫星管理器核心方法、交互逻辑):
javascript
// 卫星管理器(核心:批量创建卫星、更新轨道)
class SatelliteManager {
constructor(scene) {
this.scene = scene;
this.satellites = []; // 存储所有卫星
this.visibleSatellites = []; // 存储可见卫星
}
// 创建卫星系统(接收JSON数据,批量渲染卫星)
createSatelliteSystem(satDataList) {
satDataList.forEach((satData, index) => {
// 卫星几何体(小点状,避免遮挡)
const satGeometry = new THREE.SphereGeometry(0.8, 16, 16);
const satMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const satMesh = new THREE.Mesh(satGeometry, satMaterial);
// 计算卫星轨道位置(基于JSON中的radius、inclination参数)
const position = this.calculateSatellitePosition(satData.radius, satData.inclination, index);
satMesh.position.set(position.x, position.y, position.z);
// 绑定卫星数据(用于后续交互)
satMesh.userData.parentSatellite = { data: satData };
this.scene.add(satMesh);
this.satellites.push({ mesh: satMesh, data: satData });
});
this.visibleSatellites = [...this.satellites];
}
// 计算卫星轨道位置(简化版,基于轨道高度和倾角)
calculateSatellitePosition(radius, inclination, index) {
const angle = (index / this.satellites.length) * Math.PI * 2;
const inclinRad = inclination * Math.PI / 180;
return {
x: radius * Math.cos(angle) * Math.cos(inclinRad),
y: radius * Math.sin(inclinRad),
z: radius * Math.sin(angle) * Math.cos(inclinRad)
};
}
// 应用筛选(按国家、轨道类型筛选可见卫星)
applyFilter(filterState) {
this.visibleSatellites = this.satellites.filter(sat => {
const countryMatch = filterState.country === 'all' || sat.data.country === filterState.country;
const typeMatch = filterState.type === 'all' || sat.data.type === filterState.type;
sat.mesh.visible = countryMatch && typeMatch;
return countryMatch && typeMatch;
});
}
}
// 鼠标交互(悬停显示基础信息、点击弹窗)
function setupInteraction() {
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
// 鼠标移动:悬停卫星高亮,显示底部基础信息
renderer.domElement.addEventListener('mousemove', (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const satMeshes = satelliteManager.getVisibleSatelliteMeshes();
const intersects = raycaster.intersectObjects(satMeshes);
if (intersects.length > 0) {
const hitSatellite = intersects[0].object.userData.parentSatellite;
satelliteManager.hoverSatellite(hitSatellite); // 高亮卫星
showSelectionInfo(hitSatellite.data); // 显示底部信息
renderer.domElement.style.cursor = 'pointer';
} else {
satelliteManager.clearHover();
hideSelectionInfo();
renderer.domElement.style.cursor = 'default';
}
});
// 鼠标点击:打开卫星详情弹窗
renderer.domElement.addEventListener('click', () => {
const hovered = satelliteManager.getHoveredSatellite();
if (hovered) {
selectedSatellite = hovered;
showModal(hovered.data); // 打开弹窗
}
});
}
2.3 基础筛选功能实现(满足针对性查看需求)
航天爱好者往往希望专门查看某类卫星(如中国北斗、美国星链、气象卫星),因此实现按国家、轨道类型的筛选功能,操作简单直观。
核心示例代码(筛选逻辑):
javascript
// 筛选功能实现(绑定筛选按钮,更新筛选状态)
function setupFilters() {
const filterButtons = document.querySelectorAll('.filter-btn');
filterButtons.forEach(btn => {
btn.addEventListener('click', (e) => {
const filterType = e.target.dataset.filter; // 筛选类型(country/type)
const filterValue = e.target.dataset.value; // 筛选值(中国/气象卫星等)
// 按钮高亮切换
document.querySelectorAll(`.filter-btn[data-filter="${filterType}"]`).forEach(b => {
b.classList.remove('active');
});
e.target.classList.add('active');
// 更新筛选状态,应用筛选
filterState[filterType] = filterValue;
satelliteManager.applyFilter(filterState);
updateStats(); // 更新可见卫星数量统计
});
});
}
// 更新统计信息(显示可见卫星/总卫星数量)
function updateStats() {
document.getElementById('visible-count').textContent = satelliteManager.getVisibleCount();
document.getElementById('total-count').textContent = satelliteManager.getTotalCount();
}
代码说明:筛选按钮通过data属性绑定筛选类型和筛选值,点击后更新筛选状态,卫星管理器根据筛选条件控制卫星显示/隐藏,同时更新统计信息,满足爱好者针对性查看需求。
三、核心优化:新增简介接口,解决信息传递痛点
基础功能实现后,发现核心痛点:爱好者能看到卫星轨迹,但无法快速了解卫星具体信息(所属国家、用途、轨道特点等)。因此,重点优化卫星弹窗,新增简介接口,接收JSON中的desc字段,实现简介展示。
3.1 需求分析:为什么需要新增简介接口?
对于航天爱好者而言,"看轨迹"只是基础需求,"了解卫星"才是核心需求。例如,看到一颗卫星在绕地球运动,爱好者会好奇:这颗卫星是哪个国家的?主要用来做什么?轨道有什么特点?传统弹窗只显示轨道参数,无法满足这一需求,因此新增简介接口势在必行。
同时,放弃"代码写死简介"的方案,选择通过JSON字段接收,原因有二:
-
灵活性:卫星数据不断更新(新卫星发射、旧卫星退役),JSON更新无需修改前端代码,维护成本极低;
-
可扩展性:后续批量导入上万颗卫星数据,通过JSON统一管理简介,为AI自动生成简介奠定基础。
3.2 接口优化与弹窗样式调整(核心代码实现)
在卫星弹窗消息接口中新增desc字段,绑定JSON中的卫星简介,同时优化弹窗UI,新增独立简介展示框,支持多行自动换行,适配100字左右的简介内容。
核心示例代码(弹窗简介接口绑定、样式优化):
javascript
// 1. 弹窗HTML结构(新增简介展示框,独立模块)
{/* 卫星详情弹窗 */}
<div id="satellite-modal" class="satellite-modal">
<div class="modal-content">
<button id="modal-close-btn" class="modal-close">×</button>
<h3 id="modal-satellite-name" class="modal-title"></h3>
<div class="modal-info">
<div class="info-item">
<span class="modal-label">国家/地区:</span>
<span id="modal-country" class="modal-value"></span>
</div>
<div class="info-item">
<span class="modal-label">卫星类型:</span>
<span id="modal-type" class="modal-value"></span>
</div>
<div class="info-item">
<span class="modal-label">轨道高度:</span>
<span id="modal-altitude" class="modal-value"></span>
</div>
<div class="info-item">
<span class="modal-label">轨道倾角:</span>
<span id="modal-inclination" class="modal-value"></span>
</div>
<div class="info-item">
<span class="modal-label">运行速度:</span>
<span id="modal-speed" class="modal-value"></span>
</div>
{/* 新增简介展示模块 */}
<div class="info-item intro-item">
<span class="modal-label">卫星简介:</span>
<div id="modal-intro-text" class="modal-intro"></div>
</div>
</div>
</div>
</div>
// 2. CSS样式(简介框优化,支持多行换行、舒适行距)
<style>
.satellite-modal .modal-intro {
width: 100%;
min-height: 80px; /* 适配100字左右内容 */
padding: 8px 12px;
border: 1px solid #eee;
border-radius: 4px;
margin-top: 4px;
line-height: 1.6; /* 舒适行距 */
white-space: pre-wrap; /* 自动换行 */
word-wrap: break-word; /* 防止长文本溢出 */
color: #333;
font-size: 14px;
}
.intro-item {
margin-top: 12px;
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
// 3. JS接口绑定(接收JSON中的desc字段,渲染到弹窗)
function showModal(satData) {
const modal = document.getElementById('satellite-modal');
const overlay = document.getElementById('modal-overlay');
// 填充基础参数
document.getElementById('modal-satellite-name').textContent = satData.name;
const countryEl = document.getElementById('modal-country');
countryEl.textContent = satData.country;
// 国家样式映射(已添加韩国,避免样式异常)
const countryClassMap = {
'中国': 'cn', '美国': 'us', '俄罗斯': 'ru',
'欧盟': 'eu', '日本': 'jp', '韩国': 'kr',
'印度': 'in'
};
countryEl.className = `modal-value country-${countryClassMap[satData.country] || 'other'}`;
document.getElementById('modal-type').textContent = satData.type;
document.getElementById('modal-altitude').textContent = `${satData.radius} km`;
document.getElementById('modal-inclination').textContent = `${satData.inclination}°`;
document.getElementById('modal-speed').textContent = `${(satData.speed * 1000).toFixed(2)} rad/s`;
// 核心:接收JSON中的desc字段,渲染到简介框
document.getElementById('modal-intro-text').textContent = satData.desc;
// 显示弹窗
modal.classList.add('show');
overlay.classList.add('show');
}
代码说明:新增的简介模块独立于基础参数,样式上支持多行换行和舒适行距,确保100字左右的简介能清晰显示;JS代码中通过satData.desc绑定JSON中的简介字段,实现"JSON填值、前端自动显示"的效果,完全适配后续AI批量生成的简介数据。
四、关键升级:搭建AI自动化工作流,批量生成卫星简介
新增简介接口后,面临一个新问题:全球在轨活跃卫星有上万颗,手动编写每颗卫星的简介耗时耗力,且易出现信息不准确的问题。结合AI技术,搭建自动化工作流,实现"TLE爬取→解析→AI生成简介→JSON输出"全流程自动化,高效解决简介生成难题。
4.1 工作流核心逻辑与设计思路
工作流的核心目标是"解放双手,确保简介专业、准确",适配航天爱好者对专业信息的需求。整体逻辑如下:
「CelesTrak TLE数据爬取」→「TLE数据解析(提取核心参数)」→「AI调用(生成专业简介)」→「JSON整合输出(适配前端接口)」
设计思路贴合项目需求:
-
数据源选择CelesTrak:全球最权威的卫星轨道数据平台,每天更新4-6次,数据来自美国太空军第18太空防御中队,准确性有保障,为航天爱好者提供真实的卫星数据;
-
选择活跃卫星数据:避免爬取太空垃圾、退役卫星,让可视化场景更简洁,同时提升卫星简介的参考价值;
-
AI提示词优化:引导AI生成包含"国家、类型、轨道特点、用途"的简介,长度控制在90-110字,语言专业且易懂,贴合航天爱好者的认知水平。
4.2 AI工作流完整代码实现(Python)
以下代码实现了工作流全流程,包含TLE爬取、解析、AI调用、JSON生成,添加了限流、重试机制,确保稳定运行,可直接复制运行。
python
import requests
from sgp4.api import Satrec, SatrecArray
from tqdm import tqdm
import json
import time
from typing import List, Dict
# -------------------------- 配置参数 --------------------------
# 豆包API配置(替换为自己的API Key)
API_KEY = "你的豆包API Key"
API_URL = "https://aquasearch.ai/api/v1/chat/completions"
# CelesTrak活跃卫星TLE数据地址(只爬取活跃卫星,适配爱好者需求)
CELESTRAK_URL = "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=tle"
# 输出JSON文件路径(前端可直接加载)
OUTPUT_JSON_PATH = "satellites.json"
# 限流配置(避免API封禁、CelesTrak反爬)
REQUEST_INTERVAL = 0.5 # AI调用间隔(秒)
RETRY_TIMES = 3 # 重试次数
# -------------------------- 工具函数 --------------------------
def crawl_tle_data(url: str) -> List[str]:
"""爬取CelesTrak的TLE数据"""
try:
response = requests.get(url, timeout=10)
response.raise_for_status() # 抛出HTTP错误
tle_lines = response.text.strip().split('\n')
# 过滤无效行,TLE数据为3行一组(名称+两行轨道数据)
tle_data = []
for i in range(0, len(tle_lines), 3):
if i + 2 < len(tle_lines):
name = tle_lines[i].strip()
line1 = tle_lines[i+1].strip()
line2 = tle_lines[i+2].strip()
if line1.startswith('1') and line2.startswith('2'): # 验证TLE格式
tle_data.append((name, line1, line2))
print(f"成功爬取TLE数据,共{len(tle_data)}颗卫星")
return tle_data
except Exception as e:
print(f"爬取TLE数据失败:{str(e)}")
raise
def parse_tle_data(tle_data: List[tuple]) -> List[Dict]:
"""解析TLE数据,提取前端所需核心参数"""
parsed_satellites = []
# 批量解析TLE(提升效率)
sat_array = SatrecArray()
sat_names = []
for name, line1, line2 in tle_data:
sat = Satrec()
sat.from_lines(line1, line2)
sat_array.append(sat)
sat_names.append(name)
# 计算轨道参数(简化版,适配前端需求)
for i, name in enumerate(tqdm(sat_names, desc="解析TLE数据")):
sat = sat_array[i]
# 轨道高度(km):轨道半长轴 - 地球半径(6371km)
radius = (sat.a * 6371) - 6371 # sat.a为地球半径倍数
# 轨道倾角(°)
inclination = sat.inclo * 180 / 3.1415926535
# 判断轨道类型(适配前端筛选)
if radius < 1000:
orbit_type = "LEO(近地轨道)"
elif 1000 <= radius < 35786:
orbit_type = "MEO(中地球轨道)"
elif abs(radius - 35786) < 100:
orbit_type = "GEO(地球同步轨道)"
elif 35000 <= radius < 36000 and 40 <= inclination <= 60:
orbit_type = "IGSO(倾斜同步轨道)"
else:
orbit_type = "其他轨道"
parsed_satellites.append({
"id": f"sat_{i+1}",
"name": name,
"radius": round(radius, 2),
"inclination": round(inclination, 2),
"speed": round(sat.no_kozai / 60, 6), # 运行速度(rad/s)
"orbit_type": orbit_type,
"desc": "" # 预留desc字段,后续AI填充
})
return parsed_satellites
def generate_sat_desc(sat_info: Dict) -> str:
"""调用豆包API,生成卫星简介"""
prompt = f"""你是专业的卫星数据介绍生成助手,面向航天爱好者,语言专业且易懂,避免过于晦涩的术语。
请根据以下卫星信息,生成100字左右的简介,必须包含:卫星所属国家、类型、轨道特点、主要用途。
卫星信息:
名称:{sat_info['name']}
轨道高度:{sat_info['radius']}km
轨道类型:{sat_info['orbit_type']}
轨道倾角:{sat_info['inclination']}°
要求:
1. 长度90-110字,语言正式、流畅;
2. 国家只能是:中国/美国/俄罗斯/欧盟/日本/韩国/印度/多国家;
3. 类型只能是:气象卫星/遥感卫星/导航卫星/通信卫星/军事卫星/科学卫星/技术试验卫星;
4. 只返回简介文本,不要多余解释、不要JSON格式。"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
payload = {
"model": "doubao-pro",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.7,
"max_tokens": 150
}
# 重试机制,避免API调用失败
for _ in range(RETRY_TIMES):
try:
time.sleep(REQUEST_INTERVAL)
response = requests.post(API_URL, json=payload, timeout=15)
response.raise_for_status()
result = response.json()
desc = result["choices"][0]["message"]["content"].strip()
# 验证简介长度,不符合则重新生成
if 90 <= len(desc) <= 110:
return desc
else:
continue
except Exception as e:
print(f"AI调用失败,重试中:{str(e)}")
time.sleep(1)
print(f"AI调用多次失败,跳过该卫星:{sat_info['name']}")
return "该卫星简介暂未生成"
def generate_sat_json(parsed_satellites: List[Dict]) -> None:
"""整合解析后的数据和AI生成的简介,输出JSON文件(适配前端接口)"""
final_satellites = []
for sat in tqdm(parsed_satellites, desc="AI生成简介并整合JSON"):
# 调用AI生成简介
sat["desc"] = generate_sat_desc(sat)
# 补充前端所需的country和type字段(AI生成简介中已包含,此处简化提取)
# 实际可通过AI返回结果提取,此处为简化示例
if "中国" in sat["desc"]:
sat["country"] = "中国"
elif "美国" in sat["desc"]:
sat["country"] = "美国"
elif "俄罗斯" in sat["desc"]:
sat["country"] = "俄罗斯"
elif "欧盟" in sat["desc"]:
sat["country"] = "欧盟"
elif "日本" in sat["desc"]:
sat["country"] = "日本"
elif "韩国" in sat["desc"]:
sat["country"] = "韩国"
elif "印度" in sat["desc"]:
sat["country"] = "印度"
else:
sat["country"] = "多国家"
if "气象" in sat["desc"]:
sat["type"] = "气象卫星"
elif "导航" in sat["desc"]:
sat["type"] = "导航卫星"
elif "通信" in sat["desc"]:
sat["type"] = "通信卫星"
elif "遥感" in sat["desc"]:
sat["type"] = "遥感卫星"
elif "军事" in sat["desc"]:
sat["type"] = "军事卫星"
elif "科学" in sat["desc"]:
sat["type"] = "科学卫星"
else:
sat["type"] = "技术试验卫星"
# 保留前端所需字段,删除多余字段
final_sat = {
"id": sat["id"],
"name": sat["name"],
"radius": sat["radius"],
"inclination": sat["inclination"],
"country": sat["country"],
"type": sat["type"],
"desc": sat["desc"],
"speed": sat["speed"]
}
final_satellites.append(final_sat)
# 写入JSON文件
with open(OUTPUT_JSON_PATH, "w", encoding="utf-8") as f:
json.dump(final_satellites, f, ensure_ascii=False, indent=4)
print(f"JSON文件生成完成,路径:{OUTPUT_JSON_PATH},共{len(final_satellites)}颗卫星")
# -------------------------- 工作流主函数 --------------------------
def main():
try:
print("="*50)
print("开始卫星数据自动化处理工作流")
print("="*50)
# 1. 爬取TLE数据
tle_data = crawl_tle_data(CELESTRAK_URL)
if not tle_data:
print("无有效TLE数据,退出工作流")
return
# 2. 解析TLE数据
parsed_satellites = parse_tle_data(tle_data)
# 3. AI生成简介并整合JSON
generate_sat_json(parsed_satellites)
print("="*50)
print("工作流执行完成,JSON文件可直接用于前端加载")
print("="*50)
except Exception as e:
print(f"工作流执行失败:{str(e)}")
if __name__ == "__main__":
main()
4.3 工作流关键说明(适配航天爱好者需求)
-
限流与重试机制:避免爬取CelesTrak时被反爬,同时防止AI API被限流,确保工作流稳定运行;
-
轨道类型判断:通过轨道高度和倾角,自动区分LEO、GEO、MEO、IGSO四种常见轨道类型,与前端筛选功能对应,方便爱好者按轨道类型查看;
-
AI简介优化:提示词明确要求简介包含国家、类型、轨道特点、用途,语言专业且易懂,贴合航天爱好者的认知水平,避免过于晦涩的专业术语;
-
JSON格式适配:输出的JSON字段与前端接口完全兼容,无需前端修改代码,放入指定目录即可自动加载,实现"一键对接"。
五、项目推进总结与后续规划
本次项目推进围绕"服务航天爱好者"这一核心目标,完成了从基础搭建到核心优化、关键升级的全流程,每一步操作都贴合爱好者的使用痛点,同时通过示例代码确保项目可落地、可复用。
5.1 项目推进总结(已完成工作)
-
基础搭建:完成3D地球场景、星空背景、卫星轨迹渲染,实现鼠标交互和基础筛选功能,满足爱好者"直观查看卫星轨迹"的需求;
-
核心优化:新增简介接口,绑定JSON中的desc字段,优化弹窗样式,解决"信息传递不足"的痛点,让爱好者能快速了解卫星核心信息;
-
关键升级:搭建AI自动化工作流,实现TLE数据爬取、解析、AI简介生成、JSON输出全流程自动化,高效解决上万颗卫星简介的生成难题;
-
细节优化:添加韩国国家样式映射,确保筛选和弹窗样式正常;优化AI提示词,确保简介专业、易懂;加入限流、重试机制,提升工作流稳定性。
通过本次推进,项目已实现核心功能:3D可视化展示、卫星筛选、弹窗简介展示,完全满足航天爱好者的基础需求,同时具备可扩展性,为后续优化奠定了基础。
5.2 存在的不足
-
AI工作流效率:批量处理上万颗卫星数据时耗时较长,后续可优化并发处理逻辑,提升效率;
-
简介个性化:目前AI生成的简介模板化程度较高,后续可结合卫星具体用途,生成更具针对性的简介;
-
前端交互体验:可增加卫星轨迹高亮、鼠标悬停详细提示、轨迹历史回放等功能,进一步提升爱好者的使用体验。
5.3 后续规划(持续优化,贴合航天爱好者需求)
-
优化AI工作流:引入并发处理,提升批量数据处理效率;优化AI提示词,结合更多航天知识库,提升简介准确性和个性化;
-
丰富前端功能:增加卫星轨迹历史回放、轨迹高亮、卫星分类标签等功能,满足爱好者的深度查看需求;
-
数据更新机制:添加JSON数据自动更新功能,定期爬取CelesTrak最新数据,确保卫星信息的时效性;
-
用户体验优化:优化弹窗样式,增加简介排版美化;适配移动端,让爱好者随时随地查看卫星轨迹和信息。
六、总结
本次卫星3D可视化项目推进,核心是"以航天爱好者需求为导向",将复杂的卫星数据转化为直观、易用的可视化工具,同时通过AI技术解决批量简介生成的难题,让爱好者既能"看轨迹",也能"懂卫星"。
整个项目推进过程中,我不仅提升了前端开发、Python爬虫和AI接口调用能力,更深刻理解了"以用户需求为核心"的开发理念------对于面向航天爱好者的项目,专业、直观、易用是关键,既要保证数据的准确性和专业性,又要避免过于复杂的操作,让不同层次的航天爱好者都能轻松使用。
后续,我将继续深耕航天可视化领域,结合AI技术和航天知识,不断优化项目,希望能为航天爱好者提供更全面、更直观、更易用的卫星观察工具,也希望通过这个项目,让更多人关注航天、热爱航天,感受太空探索的无限魅力。
关注我,后续持续更新 AI 工作流实操落地、前端交互优化细节,带你从零打造可直接上线的卫星 3D 可视化平台,解锁航天可视化更多玩法~