Python控制STK的时空建模与数据获取精要

发布时间:2026/10/16 2:11:46
Python控制STK的时空建模与数据获取精要
1. 这不是“调个API”那么简单为什么Python与STK交互常被低估为“脚本小活”很多人第一次看到“Python控制STK”这个需求时下意识反应是“不就是写个Python脚本调用一下STK的COM接口网上教程一搜一堆。”我最初也这么想——直到在某高校航天动力学仿真实验室参与一个轨道交会对接模拟项目时被连续三天卡在同一个问题上Python脚本能成功创建卫星、设置初始轨道、启动仿真但每次调用GetVectorData获取目标相对位置时返回的数据全是零。STK界面里明明显示轨迹正常演进数据导出窗口也能手动导出CSV唯独Python拿不到实时向量。后来才发现问题根本不在代码语法而在于对STK底层执行模型的误判STK的COM接口并非“即发即得”的同步调用它依赖一个隐式的时间推进引擎状态机——你必须显式触发ExecuteCommand(Animate *)或Root.ExecuteCommand(Animate *)并等待动画帧真正渲染完成GetVectorData才能读取到非空结果。这背后涉及STK的双线程架构UI主线程负责图形渲染与用户交互而COM接口调用实际运行在独立的后台计算线程二者通过内部事件队列同步。若未主动推进时间轴后台线程永远停留在初始时刻t0。这种认知偏差非常典型。STKSystems Tool Kit本质上不是一个传统意义上的“软件库”而是一个具备完整时空建模能力的仿真内核可视化前端的复合体。它的Python交互核心难点从来不是语法或连接方式而是如何让Python这个外部控制器精准地“踩准”STK内部的时间节拍、对象生命周期和数据就绪状态。关键词如“创建场景”“目标参数”“获取数据”表面看是四个孤立动作实则构成一条强耦合的时空链路场景定义了时空坐标系基准目标在该坐标系中被实例化并赋予运动学属性参数计算依赖于目标在特定时刻的状态快照而数据获取则是对这一快照的原子级读取。任何一个环节的时间锚点错位整条链路就会断裂。这也是为什么大量初学者能跑通“Hello World”级别的卫星创建却在做轨道预报误差分析或传感器覆盖统计时频频失败——他们写的不是“控制脚本”而是一串缺乏时空语义的命令堆砌。我后来梳理出一套判断标准如果一个Python-STK项目其核心逻辑能被简化为“先A再B再C”且每个步骤之间没有显式的时间推进、状态校验或数据就绪等待那它大概率在复杂场景下会失效。真正的工程级交互必须把“时间”当作第一等公民来对待。比如创建一个地球同步轨道目标后不能立刻调用GetParameter查其高度必须先执行Animate *推进至少一帧再用GetTime确认当前仿真时间已更新最后才读取参数。这个看似繁琐的三步曲恰恰是STK仿真内核对物理世界“因果律”的忠实映射。本文接下来要拆解的正是这条时空链路上每一个关键节点的精确操作逻辑、常见陷阱以及如何用Python构建一套鲁棒的、可复用的STK交互层。2. 从零启动STK Python环境搭建与COM连接的“静默陷阱”STK的Python交互官方推荐路径是通过其内置的STKPython模块但这仅适用于STK安装目录下的Python解释器。而绝大多数工程实践场景中我们使用的是Anaconda或系统级Python环境此时必须依赖Windows原生的COM机制。这一步看似简单——import win32com.clientobj win32com.client.Dispatch(STK12.Application)——但背后埋藏着三个极易被忽略的“静默陷阱”它们不会报错却会让后续所有操作无声失效。2.1 陷阱一STK版本号硬编码导致的“假连接”STK的COM ProgIDProgrammatic Identifier并非固定为STK12.Application。它由STK主版本号决定STK 11对应STK11.ApplicationSTK 12对应STK12.ApplicationSTK 13对应STK13.Application。很多教程直接写死STK12.Application当用户实际安装的是STK 13时Dispatch调用会静默创建一个新实例但该实例处于“无GUI模式”即没有可视化窗口所有Root.NewScenario创建的场景也无法在界面上显示。更致命的是GetVectorData等方法仍能返回数据只是这些数据基于一个未初始化的、空的默认场景导致结果全为零。解决方案是动态探测通过win32com.client.gencache.EnsureDispatch(STK12.Application)尝试生成缓存捕获pywintypes.com_error异常后按版本号递减顺序重试直至成功。我封装了一个健壮的连接函数import win32com.client import pywintypes def connect_to_stk(max_version13, min_version11): 动态连接STK自动探测可用版本 for version in range(max_version, min_version - 1, -1): try: progid fSTK{version}.Application app win32com.client.Dispatch(progid) # 验证连接有效性尝试获取根对象 root app.Personality2 print(f成功连接 STK {version} 版本) return app, root except (pywintypes.com_error, OSError) as e: continue raise RuntimeError(无法连接到任何可用版本的STK)2.2 陷阱二STK实例的“前台抢占”与多实例冲突STK的COM接口设计为单实例Single Instance。当你用Dispatch(STK12.Application)时如果系统中已存在一个STK进程COM会自动连接到该进程而非新建一个。这本是优点但带来一个隐蔽问题如果该已有进程正被另一个用户或脚本占用你的Python脚本将共享其全部状态。例如对方刚删除了Scenario1而你的脚本试图Root.LoadScenario(Scenario1)就会失败。更糟的是Root.NewScenario(MyScene)在已有同名场景时不会报错而是静默切换到该场景并清空其内容。规避策略是强制创建新实例使用win32com.client.DispatchEx(STK12.Application)它总是启动一个全新的、独立的STK进程。但需注意DispatchEx创建的实例默认无GUI必须显式调用app.Visible True才能显示窗口。我在实际项目中为避免干扰一律采用DispatchEx并管理进程生命周期import os import subprocess def launch_fresh_stk(version12): 启动一个全新的、独立的STK实例 stk_path fC:\\Program Files\\AGI\\STK {version}\\bin\\STK.exe if not os.path.exists(stk_path): raise FileNotFoundError(fSTK {version} 未安装于 {stk_path}) # 启动STK不加载任何场景 subprocess.Popen([stk_path, -noscenario]) # 等待STK启动完成简单轮询 import time time.sleep(3) app win32com.client.DispatchEx(fSTK{version}.Application) app.Visible True return app, app.Personality22.3 陷阱三Python COM对象的“悬挂引用”与内存泄漏这是最危险的陷阱。Python通过win32com调用COM对象时会为每个COM接口创建一个Python代理对象。当脚本结束或对象被del时若STK端仍有对该对象的引用例如你将一个Satellite对象赋值给Root的某个属性Python的垃圾回收器无法释放该代理导致STK进程无法正常退出后台线程持续占用CPU。多次运行后系统中会残留多个“僵尸”STK进程最终耗尽资源。根本解决法是显式释放所有COM对象在脚本退出前对所有win32com.client.CDispatch类型的变量调用pythoncom.CoUninitialize()并确保所有STK对象引用被置为None。我习惯在try...finally块中统一处理import pythoncom def safe_stk_cleanup(app, root, *objects): 安全清理STK COM对象 # 先释放所有业务对象 for obj in objects: if hasattr(obj, _oleobj_): del obj._oleobj_ # 清空引用 for obj in [app, root] list(objects): if obj is not None: obj None # 最终释放COM库 pythoncom.CoUninitialize() # 使用示例 app, root connect_to_stk() try: scenario root.NewScenario(MyScenario) satellite scenario.Children.New(Satellite, MySat) # ... 执行其他操作 finally: safe_stk_cleanup(app, root, scenario, satellite)这三个陷阱每一个都曾让我在凌晨两点对着空白的STK窗口抓狂。它们不报错却让整个交互流程变成一场与幽灵的搏斗。记住STK的COM连接不是一次性的握手而是一场需要全程监护的共生关系。3. 场景与目标的时空建模从“创建”到“就绪”的七步精控在STK中“创建”一个场景或目标远非NewScenario或New方法调用那么简单。它是一个分阶段的、需要严格遵循时空逻辑的初始化过程。我将其归纳为“七步精控”每一步都对应STK内核的一个关键状态跃迁。跳过任何一步后续的数据获取必然失败。3.1 第一步场景创建与时间系统锚定Root.NewScenario(MyScene)只是创建了一个空壳。真正的起点是时间系统配置。STK默认使用UTC时间但许多轨道数据源如TLE使用J2000历元。若不显式设置GetVectorData返回的向量将基于错误的时间参考系导致位置计算偏差达数百公里。必须立即调用scenario.SetTimePeriod和scenario.SetTimeSystemscenario root.NewScenario(OrbitAnalysis) # 设置仿真时间范围从2024年1月1日00:00 UTC开始持续24小时 scenario.SetTimePeriod(1 Jan 2024 00:00:00.000, 2 Jan 2024 00:00:00.000) # 强制使用UTC时间系统避免J2000等混淆 scenario.SetTimeSystem(UTC) # 关键必须调用UpdateAll以使时间设置生效 scenario.UpdateAll()提示UpdateAll()是STK中最容易被遗忘的“魔法按钮”。它强制STK重新计算所有对象的内部状态包括时间系统、坐标系转换矩阵等。没有它后续所有操作都基于旧的、未更新的上下文。3.2 第二步坐标系与参考框架绑定STK中所有目标的位置、速度都相对于某个参考框架Reference Frame如Earth/ICRF国际天球参考系或Earth/Fixed地固系。创建目标时必须明确指定其参考框架否则STK会使用默认的、可能不匹配的框架。例如导入TLE数据时TLE天然属于Earth/ICRF而你想计算其对地面站的仰角则需在Earth/Fixed下进行。因此在创建目标后必须立即为其设置CentralBody和ReferenceFramesatellite scenario.Children.New(Satellite, ISS) # 设置中心天体为Earth satellite.SetCentralBody(Earth) # 设置参考框架为Earth/ICRFTLE标准 satellite.SetReferenceFrame(Earth/ICRF)3.3 第三步目标运动学属性注入这是核心。STK支持多种运动学模型TLE、Two-Body、Custom Orbit等。对于TLE不能直接用字符串赋值必须通过Propagator对象# 获取卫星的Propagator对象 prop satellite.Propagator # 设置为TLE传播器 prop.SetPropagatorType(TLE) # 加载TLE行 tle_line1 1 25544U 98067A 24001.50000000 .00000000 00000-0 00000-0 0 9999 tle_line2 2 25544 51.6431 121.4122 0003422 20.2222 10.2222 15.49822222222222 prop.Tle.Line1 tle_line1 prop.Tle.Line2 tle_line2 # 关键必须调用Propagate否则TLE数据不会被解析 prop.Propagate()注意Propagate()调用后STK会根据TLE计算出初始历元下的位置和速度并存储在内部。但此时目标尚未“激活”其轨迹还未生成。3.4 第四步轨迹生成与时间轴激活Propagate()只计算初始状态。要生成完整的轨迹曲线必须调用UpdateAll()并确保场景时间轴已“激活”# 激活场景时间轴关键 scenario.SetTimePeriod(1 Jan 2024 00:00:00.000, 2 Jan 2024 00:00:00.000) scenario.UpdateAll() # 此时STK会为卫星生成完整的轨迹数据3.5 第五步动画引擎启动与首帧渲染这是数据获取前的最后屏障。GetVectorData读取的是动画引擎当前渲染帧所对应时刻的数据。若动画未启动引擎停留在t0所有数据均为初始值。必须显式启动动画# 启动动画从起始时间开始 root.ExecuteCommand(Animate *) # 等待首帧渲染完成简单轮询生产环境建议用事件回调 import time time.sleep(0.5) # 给STK足够时间渲染第一帧3.6 第六步目标状态就绪校验不要假设Propagate()和UpdateAll()后目标就绪。必须用STK的IsPropagated属性进行校验if not satellite.IsPropagated: raise RuntimeError(f卫星 {satellite.InstanceName} 未成功传播请检查TLE或时间设置)3.7 第七步坐标系转换预热若需在非默认坐标系下获取数据如将ICRF下的位置转为地固系STK需要预先“预热”转换矩阵。直接调用GetVectorData可能返回None。应先执行一次空转换# 预热Earth/Fixed坐标系转换 satellite.GetVectorData(Position, Earth/Fixed, 1 Jan 2024 00:00:00.000)这七步是我从数十个失败案例中提炼出的最小完备序列。它不是教科书式的理想流程而是STK内核真实执行的、不可绕过的状态机路径。每一次UpdateAll()、Propagate()、ExecuteCommand(Animate *)都是对STK内部状态的一次“叩门”只有门开了数据才能流出。4. 目标参数计算与数据获取从“能拿到”到“拿得准”的精度控制当场景与目标就绪后“计算参数并获取数据”看似水到渠成实则暗流汹涌。STK提供了海量的参数GetParameter和向量GetVectorData但不同参数的计算时机、精度来源、坐标系依赖各不相同。一个常见的误区是认为GetVectorData(Position)返回的就是“绝对位置”而忽略了其背后复杂的时空采样逻辑。4.1 参数类型辨析静态、动态与派生STK参数分为三类获取方式截然不同静态参数Static Parameters如Name、CentralBody、Mass。这些是对象的固有属性调用GetParameter即可即时返回无需时间戳。动态参数Dynamic Parameters如Altitude、VelocityMagnitude、ElevationAngle。这些值随时间变化必须提供精确的时间点1 Jan 2024 00:00:00.000或时间范围1 Jan 2024 00:00:00.000, 2 Jan 2024 00:00:00.000。关键陷阱若时间点不在STK已计算的轨迹点上STK会进行线性插值精度取决于轨迹点密度。默认轨迹点间隔为60秒对于高动态目标如近地轨道插值误差可达数公里。派生参数Derived Parameters如Access访问时间、Coverage覆盖区域。这些不是直接存储的而是STK在后台实时计算的。调用GetParameter时STK会触发一次完整的计算流程耗时较长且结果受当前场景时间范围影响。4.2 向量数据获取坐标系、时间与采样率的三角博弈GetVectorData是获取位置、速度、视线向量的核心方法但其参数组合决定了数据的物理意义# 语法GetVectorData(VectorType, ReferenceFrame, TimeString) # 示例获取ISS在ICRF下的位置向量单位km pos_vec satellite.GetVectorData(Position, Earth/ICRF, 1 Jan 2024 00:00:00.000) # 示例获取ISS相对于某地面站的视线向量单位km ground_station scenario.Children.New(Place, Beijing) ground_station.SetPosition(Geodetic, 39.9042, 116.4074, 0.0) # 纬度、经度、高度 # 计算从地面站指向ISS的向量 line_of_sight ground_station.GetVectorData(To, Earth/ICRF, 1 Jan 2024 00:00:00.000, satellite.InstanceName)这里存在三个维度的精密控制坐标系选择Earth/ICRF给出惯性系下的绝对位置Earth/Fixed给出地固系下的位置用于计算经纬度Vehicle/Body给出卫星本体坐标系下的向量用于传感器指向。选错坐标系数据物理意义全错。时间精度STK内部时间精度为毫秒级。若传入1 Jan 2024 00:00:00无毫秒STK会截断为1 Jan 2024 00:00:00.000但若轨迹点恰好在00:00:00.500则会插值。最佳实践是使用GetTime()获取当前动画帧的精确时间current_time root.CurrentTime # 返回如 1 Jan 2024 00:00:00.123 pos_vec satellite.GetVectorData(Position, Earth/ICRF, current_time)采样率控制GetVectorData默认返回单点。若需批量数据可传入时间范围字符串并指定采样间隔# 获取从t0到t1每10秒一个点的位置向量 time_range 1 Jan 2024 00:00:00.000, 1 Jan 2024 01:00:00.000 pos_data satellite.GetVectorData(Position, Earth/ICRF, time_range, 10) # 返回一个二维数组[[x0,y0,z0], [x1,y1,z1], ...]注意10表示10秒间隔但STK会自动对齐到其内部轨迹点网格。若原始轨迹点间隔为60秒10将被忽略仍按60秒返回。要获得高密度数据必须先提高轨迹点密度satellite.Propagator.SetPropagatorOption(StepSize, 10)然后Propagate()。4.3 数据精度验证用STK自身做“黄金标准”在工程实践中我从不信任第一次获取的数据。必须用STK的GUI功能进行交叉验证在STK界面中右键点击卫星 -Properties-3D Graphics-Show Position Vector观察向量是否正确指向。右键 -Report Graph Manager- 新建一个Vector报告选择Position导出为CSV。将Python脚本导出的数据与CSV文件逐行比对。我编写了一个简单的验证函数def validate_vector_data(python_data, csv_file_path, tolerance_km0.001): 验证Python获取的向量数据与STK导出CSV的一致性 import pandas as pd csv_data pd.read_csv(csv_file_path) # CSV中Position列为x,y,z格式需分割 csv_xyz csv_data[Position].str.split(,, expandTrue).astype(float) for i, (py_vec, csv_vec) in enumerate(zip(python_data, csv_xyz.values)): error np.linalg.norm(np.array(py_vec) - csv_vec) if error tolerance_km: print(f第{i}点误差超限{error:.6f} km) return False print(数据验证通过) return True这个验证步骤帮我揪出了两次因ReferenceFrame设置错误导致的系统性偏差。它不是多余的仪式而是建立数据可信度的基石。5. 实战排障从“数据全为零”到“毫秒级精度”的完整排查链路在某次为某实验室开发轨道预报误差分析工具时我遇到了一个经典问题脚本运行后GetVectorData返回的[0.0, 0.0, 0.0]而STK界面中卫星轨迹清晰可见。以下是完整的、可复现的排查链路它展示了如何像调试一个黑盒系统一样层层剥开STK的内部状态。5.1 第一层连接与可见性诊断首先排除最基础的连接问题# 1. 检查STK应用是否真的可见 print(fSTK Visible: {app.Visible}) # 应为True # 2. 检查当前场景是否存在 print(fCurrent Scenario: {root.CurrentScenario}) # 应为MyScenario # 3. 检查卫星是否在场景子对象中 children [c.InstanceName for c in scenario.Children] print(fScenario Children: {children}) # 应包含ISS若app.Visible为False说明DispatchEx创建的实例未显示若CurrentScenario为空说明NewScenario未被正确激活。5.2 第二层时间轴与动画状态诊断这是“数据为零”的最大嫌疑区# 1. 检查当前时间 print(fCurrent Time: {root.CurrentTime}) print(fScenario Start: {scenario.StartTime}) print(fScenario Stop: {scenario.StopTime}) # 2. 检查动画是否正在运行 anim_state root.ExecuteCommand(Animate State) print(fAnimation State: {anim_state}) # 应返回Running或Paused # 3. 检查卫星是否已传播 print(fISS IsPropagated: {satellite.IsPropagated})若CurrentTime仍为1 Jan 2024 00:00:00.000初始值而anim_state为Stopped则问题根源在此。必须执行root.ExecuteCommand(Animate *)并等待。5.3 第三层轨迹数据存在性诊断即使动画运行轨迹数据也可能未生成# 1. 获取卫星的轨迹点数量 track_points satellite.DataProviders.Item(Orbit).Group.Item(Position).Exec() print(fTrajectory Points Count: {len(track_points)}) # 2. 获取第一个轨迹点的时间和位置 if len(track_points) 0: first_point track_points[0] print(fFirst Point Time: {first_point.Time}) print(fFirst Point Position: {first_point.Value})若track_points为空说明Propagate()未成功或UpdateAll()未调用。此时应回溯到第三章的七步精控逐一检查。5.4 第四层坐标系与向量类型诊断GetVectorData返回零往往是因为请求的向量在指定坐标系下不存在# 1. 列出卫星支持的所有向量类型 vector_types satellite.DataProviders.Item(Vectors).Group print(Available Vector Types:) for vt in vector_types: print(f - {vt.Name}) # 2. 列出卫星支持的所有参考框架 ref_frames satellite.DataProviders.Item(Vectors).Group.Item(Position).Exec() print(Available Reference Frames for Position:) for rf in ref_frames: print(f - {rf.Name})若Earth/ICRF不在列表中说明卫星的ReferenceFrame未正确设置或SetReferenceFrame调用失败。5.5 第五层毫秒级时间对齐诊断这是最隐蔽的陷阱。STK内部时间精度为毫秒而Python字符串拼接常丢失毫秒# 错误做法时间字符串无毫秒 bad_time 1 Jan 2024 00:00:00 # 正确做法从STK获取精确时间 good_time root.CurrentTime # 如 1 Jan 2024 00:00:00.123 # 验证时间点是否在轨迹范围内 track_start satellite.DataProviders.Item(Orbit).Group.Item(Position).Exec()[0].Time print(fTrack Start Time: {track_start}) print(fQuery Time: {good_time})若good_time早于track_startGetVectorData必返回零。此时需推进动画或调整查询时间。5.6 排查链路总结一张决策树表格排查层级关键检查项正常值异常表现解决方案连接层app.Visible,root.CurrentScenarioTrue,MyScenarioFalse,调用app.Visible True确认NewScenario返回值时间层root.CurrentTime,anim_state1 Jan 2024 00:00:00.123,Running1 Jan 2024 00:00:00.000,Stoppedroot.ExecuteCommand(Animate *)time.sleep(0.5)轨迹层len(track_points) 00检查Propagate()和UpdateAll()调用顺序坐标系层Earth/ICRFinref_framesTrueFalsesatellite.SetReferenceFrame(Earth/ICRF)时间精度层good_timetrack_startTrueFalse使用root.CurrentTime作为查询时间这张表是我放在桌面的“STK急救卡”。每当数据异常我就按此顺序执行五次print90%的问题能在两分钟内定位。它不依赖猜测只依赖STK暴露的、可验证的内部状态。6. 工程化封装构建一个鲁棒、可复用的STK-Python交互层将上述所有经验沉淀为一个可复用的Python模块是提升工程效率的关键。我设计了一个名为stk_interop的轻量级封装它不追求大而全而是聚焦于解决前述所有核心痛点。其核心设计理念是状态驱动、显式时间、自动校验。6.1 核心类设计STKSessionSTKSession是整个交互层的入口它封装了连接、清理、状态管理class STKSession: def __init__(self, version12, visibleTrue, fresh_instanceTrue): self.version version self.visible visible self.fresh_instance fresh_instance self.app None self.root None self.scenario None self._connect() def _connect(self): if self.fresh_instance: self.app, self.root launch_fresh_stk(self.version) else: self.app, self.root connect_to_stk(self.version) self.app.Visible self.visible def new_scenario(self, name, start_time, stop_time, time_systemUTC): self.scenario self.root.NewScenario(name) self.scenario.SetTimePeriod(start_time, stop_time) self.scenario.SetTimeSystem(time_system) self.scenario.UpdateAll() # 立即生效 return self.scenario def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): safe_stk_cleanup(self.app, self.root)6.2 目标管理器STKTargetSTKTarget封装了目标创建、传播、状态校验的完整生命周期class STKTarget: def __init__(self, session, scenario, target_type, name): self.session session self.scenario scenario self.target scenario.Children.New(target_type, name) self.name name def set_central_body(self, body_name): self.target.SetCentralBody(body_name) def set_reference_frame(self, frame_name): self.target.SetReferenceFrame(frame_name) def propagate_tle(self, line1, line2): prop self.target.Propagator prop.SetPropagatorType(TLE) prop.Tle.Line1 line1 prop.Tle.Line2 line2 prop.Propagate() # 自动校验 if not self.target.IsPropagated: raise RuntimeError(fTLE传播失败{self.name}) def get_position(self, time_str, ref_frameEarth/ICRF): 安全获取位置自动处理坐标系预热 # 预热 self.target.GetVectorData(Position, ref_frame, time_str) return self.target.GetVectorData(Position, ref_frame, time_str) def get_access_times(self, other_target, start_time, stop_time): 计算访问时间返回[(start, end), ...]列表 access self.target.DataProviders.Item(Access).Group.Item(Access Data) result access.Exec([other_target.InstanceName, start_time, stop_time]) # 解析STK返回的复杂结构 access_list [] for i in range(result.Count): interval result.Item(i) access_list.append((interval.StartTime, interval.StopTime)) return access_list6.3 使用示例一个完整的轨道分析脚本现在一个原本需要百行、充满陷阱的脚本可以浓缩为清晰、可读的几行from stk_interop import STKSession, STKTarget # 1. 启动一个干净的STK会话 with STKSession(version12, visibleTrue) as stk: # 2. 创建场景 scenario stk.new_scenario( nameISS_Analysis, start_time1 Jan 2024 00:00:00.000, stop_time1 Jan 2024 01:00:00.000 ) # 3. 创建ISS目标 iss STKTarget(stk, scenario, Satellite, ISS) iss.set_central_body(Earth) iss.set_reference_frame(Earth/ICRF) iss.propagate_tle( 1 25544U 98067A 24001.50000000 .00000000 00000-0 00000-0 0 9999, 2 25544 51.6431 121.4122 0003422 20.2222 10.2222 15.49822222222222 ) # 4. 创建北京地面站 beijing STKTarget(stk, scenario, Place, Beijing) beijing.set_position(Geodetic, 39.9042, 116.4074, 0.0) # 5. 计算访问时间 access_times iss.get_access_times(beijing, 1 Jan 2024 00:00:00.000, 1 Jan 2024 01:00:00.000) print(f北京站访问时段: {access_times}) # 6. 获取某一时刻的精确位置 pos iss