Week 2 — 系統架構
week02_architecture/基礎知識導論
Week 2 基礎知識導論 — 系統架構與 CONOPS
Week 1 認識了物件。這一週要學的是:這些物件怎麼被「決定」出來的。
架構不是畫方塊圖,是把一句任務目標翻譯成一組可驗證的工程需求。
1. 這一週在解什麼問題
FIREYE 的任務敘述是「監測火山活動」。這句話沒有任何一個數字。
系統架構的工作,就是把它變成:
對台灣與環太平洋 12 座指定火山,在白天可見光條件下,
以優於 30 m 的地面解析度成像,單一目標重訪時間 < 24 hr,
影像於取得後 12 hr 內下傳至地面站,任務壽命 2 年。
有了這組數字,才有辦法選軌道(Week 4)、算功率(Week 7)、開鏈路(Week 11)。
沒有數字的需求,無法被違反,也就無法被設計。
2. 核心觀念
2.1 需求的三個層級
任務目標 (Mission Objective) 監測火山活動
↓ 誰來做、做到什麼程度
任務需求 (Mission Requirements) 30 m GSD / 24 hr 重訪 / 12 hr 延遲
↓ 分配到子系統
系統需求 (System Requirements) 軌道高度 500–700 km / 下傳 ≥ 1.5 GB/day / 功率 ≥ 12 W
往下拆叫 flow-down,往上追叫 traceability。每一條系統需求都要能回答
「你是從哪一條任務需求來的」,否則就是工程師自己加的。
2.2 子系統分工(本課 16 週的骨架)
| 子系統 | 縮寫 | 主要職責 | 對應週次 |
|---|---|---|---|
| 結構與機構 | STR | 承受發射載荷、提供介面 | Week 5 |
| 熱控 | TCS | 讓每個元件待在溫度範圍內 | Week 6 |
| 電力 | EPS | 發電、儲能、配電 | Week 7 |
| 姿態確定與控制 | ADCS | 指向與消旋 | Week 8 |
| 推進 | PROP | 軌道機動與維持、去軌 | Week 10 |
| 通訊 | TT&C / COMM | 遙測、遙控、資料下傳 | Week 11 |
| 指令與資料處理 | C&DH / OBC | 排程、儲存、壓縮 | Week 12, 13 |
| 酬載 | P/L | 任務本體 | Week 12 |
| 地面段 | GS | 地面站、排程、運作 | Week 14 |
耦合才是重點:酬載開機 → 功率上升 → 電池 DOD 上升 → 電池發熱 → 熱控負擔上升 →
可能要換散熱面 → 影響太陽能板面積 → 回頭影響發電。系統工程的價值就在管理這些迴圈。
2.3 CONOPS:把時間軸畫出來
CONOPS 是「衛星一天到晚在做什麼」的敘事。至少要包含:
- 模式 (Modes):Safe / Nominal / Imaging / Downlink / Detumble / Charging。
- 模式轉換條件:什麼時候切換?誰決定?(地面排程 or 自主?)
- 典型一圈的時間軸:日照 → 充電;過火山 → 成像;過地面站 → 下傳;地影 → 低功耗。
- 異常處理:失聯多久進 Safe Mode?
CONOPS 決定了功率的 duty cycle,而 duty cycle 直接決定電池大小。
Week 3 的功率預算表如果沒有 CONOPS,就只是在亂填數字。
2.4 架構權衡 (Trade Study) 的正確做法
一個可信的 trade study 需要四件事:
- 選項清單 — 至少 3 個真正不同的方案(不是同一方案的三個參數)。
- 評估準則 — 事先定義,不能看到結果才決定重視什麼。
- 量化模型 — 每個準則要能算出數字(這就是 STK/Python 的用途)。
- 敏感度 — 換一組權重,贏家會不會換人?會換就代表結論脆弱。
本課的 trade study 都以 STK 算出的物理量當準則來源,而不是靠直覺打分。
2.5 「Coverage 換 Volume」:本週的核心權衡
同一個成像需求,有兩條完全不同的路:
| 路線 | 做法 | 代價 |
|---|---|---|
| 加覆蓋 | 更高軌道、更寬視野、更多衛星 | 解析度變差 / 成本上升(Week 16) |
| 加體積 | 更大光學、更大儲存、更大天線 | 質量與功率上升,可能超出 6U(Week 3, 5, 7) |
這個權衡沒有唯一解,取決於你把哪一條需求視為不可退讓。
架構的本質就是決定「哪一條需求可以退」。
2.6 介面與 ICD
子系統之間出錯,九成出在介面。介面控制文件 (ICD) 至少要寫:
機械(螺孔、質心、公差)、電氣(電壓、電流、接頭腳位)、資料(協定、速率、封包格式)、
熱(傳導路徑、容許溫度)。
在 STK 裡的對應物就是物件的父子關係與屬性:感測器掛在衛星上、
天線掛在感測器上、地面站的仰角遮罩約束了鏈路 —— 每一層都是一個介面。
3. 名詞與符號
| 名詞 | 意義 |
|---|---|
| CONOPS | 運作概念,任務的時間軸敘事 |
| Flow-down | 需求由上而下的分解 |
| Traceability | 每條需求都追得到來源 |
| Duty cycle | 某個負載開機時間佔比 |
| Trade study | 有準則、可量化的方案比較 |
| ICD | 介面控制文件 |
| Mode | 衛星的運作模式 |
| GSD | Ground Sample Distance,地面解析度 |
| Revisit time | 重訪時間,同一目標兩次觀測的間隔 |
| Margin | 餘裕,設計值與需求值的差 |
4. 觀念 → 本週實驗對照
| 觀念 | 出現在本週的哪裡 |
|---|---|
| 物件即介面 | week02_architecture.txt 建立 Transmitter/Receiver 的掛載路徑 |
| 地面站仰角遮罩 | 腳本裡的 SetConstraint ... ElevationAngle Min 5 |
| Access 當量化準則 | 用 access 統計比較架構選項 |
| 分析窗必須夠長 | 本週原本 2.5 hr 的窗導致成像 access 為 0,已改為 24 hr |
| ACK ≠ 生效 | week02_verify.py,其中抓出「設定天線型別會沖掉先前寫的設計頻率」 |
5. 常見誤解
- 「架構圖畫完就算架構做完了。」 — 方塊圖不含任何可被違反的內容。
架構的產出是需求表 + trade study + CONOPS,圖只是索引。 - 「先做設計,需求之後補。」 — 補出來的需求一定剛好符合你已經做的設計,
等於沒有驗證。 - 「餘裕留 10% 就好。」 — 早期階段(Phase A/B)業界慣例是質量餘裕 20–30%,
功率餘裕更高。餘裕不是保守,是對未知的定量承認。 - 「access 是 0 表示不可行。」 — 先檢查分析窗長度、仰角約束、感測器錐角有沒有設對。
本教材親自踩過這個坑。
6. 延伸閱讀
系統工程
- NASA Systems Engineering Handbook(NTRS 檢索)
— 在 NTRS 搜尋 "NASA Systems Engineering Handbook SP-2016-6105",全文免費。 - ECSS 標準總表
— 歐洲太空標準。看 ECSS-E-ST-10 系列(系統工程)與 ECSS-M 系列(專案管理)。 - NASA Lessons Learned Information System
— 真實任務失敗的教訓資料庫。架構課最好的反面教材來源。
任務案例
- eoPortal 衛星任務資料庫
— 找 3 顆做火山/熱異常觀測的真實衛星(如 Landsat、Sentinel-2、FireSat 概念),
比較它們的軌道、酬載與資料架構。 - MIT OCW 16.851 Satellite Engineering
— Lecture 1–3 就是需求分解與 trade study 的標準教法。
工具
- Ansys STK 線上說明 — Access 與 Constraint 的完整清單。
- 本機:
C:\Program Files\AGI\STK 12\Help\Content\chains\chain-01.htm(Chain 物件說明)。
7. 課前自我檢核
- 把「監測火山活動」寫成三條可被違反的量化需求。
- 你的 CONOPS 裡,成像模式與下傳模式可以同時發生嗎?這個答案會影響哪一份預算表?
- 一份 trade study 的結論在權重微調後就翻盤了,這代表什麼?該怎麼處理?
- 為什麼「介面」比「元件」更容易出錯?在 STK 的物件樹裡,介面對應到什麼?
- 分析時間窗設 2.5 小時,對一顆 LEO 衛星的成像 access 統計會造成什麼系統性偏差?
程式碼
# ============================================================
# Week 2 系統架構 — 通訊、地面站、火點與 access
#
# 本週目標:把 Week 1 的 FIREYE 擴成一個「系統」——加上下行通訊、
# 地面站網路與觀測目標,再用 chain access 把「看得到」量化成
# 「能下傳多少資料」(coverage 換 volume)。
#
# 前置:必須先執行 Week 1 的 week01_eSatellite_scenario.txt,
# 本檔沒有自己的 New / Scenario,單獨執行會整支失敗。
#
# 由原本的 eSatellite_STK_Connect.txt 拆分而來(後半)。
# 驗證段:week02_verify.py
# ============================================================
# ============================================================
# SBand communications: FOV sensor + transmitter
# Tutorial: antenna boresight [0 0 1], FOV 120 deg, DataRate 10 Mbit/s
# (120 deg full cone -> 60 deg SimpleCone half-angle)
# ============================================================
New / */Satellite/FIREYE/Sensor SBandFOV
Define */Satellite/FIREYE/Sensor/SBandFOV SimpleCone 60
Graphics */Satellite/FIREYE/Sensor/SBandFOV SetColor cyan
New / */Satellite/FIREYE/Sensor/SBandFOV/Transmitter SBand
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model Complex_Transmitter_Model
# S-band downlink [ASSUME 2.25 GHz]
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.Frequency 2250000000.0
# 10 Mbit/s (tutorial)
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.DataRate 10000000.0
# W [ASSUME]
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.Power 4.0
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.AntennaControl.Antenna Isotropic
# 天線型別要先設:改 Antenna 會換掉天線物件,先前寫的 DesignFrequency
# 會被沖回 STK 預設的 14.5 GHz(ACK 照給,值卻沒了)
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.AntennaControl.Antenna.DesignFrequency 2250000000.0
Transmitter */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand SetValue Model.Modulator BPSK
# ============================================================
# Ground-station network (Esrange is REAL/named; rest REPRESENTATIVE)
# lat lon alt(km)
# ============================================================
New / */Facility Esrange_Station_KSX
SetPosition */Facility/Esrange_Station_KSX Geodetic 67.8939 21.1061 0.341
SetConstraint */Facility/Esrange_Station_KSX ElevationAngle Min 5.0
New / */Facility Svalbard_SvalSat
SetPosition */Facility/Svalbard_SvalSat Geodetic 78.2290 15.4070 0.450
SetConstraint */Facility/Svalbard_SvalSat ElevationAngle Min 5.0
New / */Facility Inuvik
SetPosition */Facility/Inuvik Geodetic 68.3200 -133.5490 0.100
SetConstraint */Facility/Inuvik ElevationAngle Min 5.0
New / */Facility Fairbanks_ASF
SetPosition */Facility/Fairbanks_ASF Geodetic 64.8600 -147.8500 0.300
SetConstraint */Facility/Fairbanks_ASF ElevationAngle Min 5.0
New / */Facility Troll_Antarctica
SetPosition */Facility/Troll_Antarctica Geodetic -72.0120 2.5350 1.270
SetConstraint */Facility/Troll_Antarctica ElevationAngle Min 5.0
New / */Facility Punta_Arenas
SetPosition */Facility/Punta_Arenas Geodetic -52.9400 -70.8500 0.030
SetConstraint */Facility/Punta_Arenas ElevationAngle Min 5.0
New / */Facility Awarua_NZ
SetPosition */Facility/Awarua_NZ Geodetic -46.5290 168.3800 0.010
SetConstraint */Facility/Awarua_NZ ElevationAngle Min 5.0
# Group stations (the real scenario ships 22 - add the rest here)
New / */Constellation GroundStations
Chains */Constellation/GroundStations Add */Facility/Esrange_Station_KSX
Chains */Constellation/GroundStations Add */Facility/Svalbard_SvalSat
Chains */Constellation/GroundStations Add */Facility/Inuvik
Chains */Constellation/GroundStations Add */Facility/Fairbanks_ASF
Chains */Constellation/GroundStations Add */Facility/Troll_Antarctica
Chains */Constellation/GroundStations Add */Facility/Punta_Arenas
Chains */Constellation/GroundStations Add */Facility/Awarua_NZ
# ============================================================
# Wildfire targets (REPRESENTATIVE 2020 fire regions)
# ============================================================
New / */Target Wildfire0
SetPosition */Target/Wildfire0 Geodetic 37.05 -119.75 0.5 # California Sierra
New / */Target Wildfire1
SetPosition */Target/Wildfire1 Geodetic 44.30 -121.20 1.0 # Oregon Cascades
New / */Target Wildfire2
SetPosition */Target/Wildfire2 Geodetic 65.50 -150.00 0.2 # Interior Alaska
New / */Target Wildfire3
SetPosition */Target/Wildfire3 Geodetic 62.00 129.00 0.2 # Sakha / Siberia Arctic
New / */Constellation Wildfires
Chains */Constellation/Wildfires Add */Target/Wildfire0
Chains */Constellation/Wildfires Add */Target/Wildfire1
Chains */Constellation/Wildfires Add */Target/Wildfire2
Chains */Constellation/Wildfires Add */Target/Wildfire3
# ============================================================
# Access analysis
# ============================================================
# Imaging: payload sees wildfires
New / */Chain Imaging
Chains */Chain/Imaging Add */Satellite/FIREYE/Sensor/Imager
Chains */Chain/Imaging Add */Constellation/Wildfires
Chains */Chain/Imaging Compute
Report_RM */Chain/Imaging Style "Complete Chain Access"
# Downlink: SBand FOV sees ground stations
# (data volume estimate = sum(access duration seconds) * 10e6 bits)
New / */Chain Downlink
Chains */Chain/Downlink Add */Satellite/FIREYE/Sensor/SBandFOV
Chains */Chain/Downlink Add */Constellation/GroundStations
Chains */Chain/Downlink Compute
Report_RM */Chain/Downlink Style "Complete Chain Access"
# Link budget (needs a ground receiver; add one per station to use):
# ReportCreate <GndRx> Type Display Style "Link Budget - Detailed" AccessObject */Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand
# -*- coding: utf-8 -*-
# ================================================================
# Week 2 驗證段 — 系統架構的物件、鏈路與 access
#
# 本週把單一衛星擴成一個系統。要驗的不只是「物件建出來了」,
# 更重要的是 chain access 真的算得出通聯 —— 沒有 access,coverage 換 volume 就是空談。
#
# 執行:python week02_verify.py
# 離開碼 0 = 全部通過,1 = 有項目對不上
# ================================================================
import os
import sys
# notebook 的 kernel 沒有 __file__。而且啟動 STK Engine 會改變 process 的
# 工作目錄,所以不能事後才靠 os.getcwd() —— notebook 開頭的 bootstrap cell
# 會先把週次目錄釘在 WEEK_DIR。
HERE = (os.path.dirname(os.path.abspath(__file__)) if "__file__" in globals()
else globals().get("WEEK_DIR") or os.path.abspath(os.getcwd()))
REPO = os.path.abspath(os.path.join(HERE, os.pardir))
sys.path.insert(0, os.path.join(REPO, "tools"))
from stk_verify import Verifier, load_lab
from agi.stk12.stkdesktop import STKDesktop
from stk_verify import run_script # noqa: E402
def main():
app = STKDesktop.AttachToApplication()
root = app.Root
if root.CurrentScenario is not None:
root.CloseScenario()
# 本週沒有自己的 New / Scenario,必須接在 Week 1 的場景上
run_script(root, os.path.join(REPO, "week01_intro",
"week01_eSatellite_scenario.txt"), echo_fail=False)
ack, nack = run_script(root, os.path.join(HERE, "week02_architecture.txt"))
v = Verifier(root)
v.auto_setvalues(os.path.join(HERE, "week02_architecture.txt"))
v.object_exists("*/Satellite/FIREYE/Sensor/SBandFOV")
v.cone_angle("*/Satellite/FIREYE/Sensor/SBandFOV", 60.0)
v.object_exists("*/Satellite/FIREYE/Sensor/SBandFOV/Transmitter/SBand")
sc = root.CurrentScenario
kinds = {}
for i in range(sc.Children.Count):
k = sc.Children.Item(i).ClassName
kinds[k] = kinds.get(k, 0) + 1
v.expect("地面站數", kinds.get("Facility", 0), 7, tol=0, unit=" 座")
v.expect("火點目標數", kinds.get("Target", 0), 4, tol=0, unit=" 個")
v.expect("Constellation 數", kinds.get("Constellation", 0), 2, tol=0, unit=" 個")
v.expect("Chain 數", kinds.get("Chain", 0), 2, tol=0, unit=" 條")
# chain access:真的算得出通聯嗎?這才是「coverage 換 volume」的前提
for chain, label in (("Imaging", "成像"), ("Downlink", "下傳")):
try:
ch = root.GetObjectFromPath(f"*/Chain/{chain}")
dp = ch.DataProviders.GetDataPrvIntervalFromPath("Complete Access")
res = dp.Exec(sc.StartTime, sc.StopTime)
durs = res.DataSets.GetDataSetByName("Duration").GetValues()
v.expect_true(f"{label} chain 有 access", len(durs) > 0,
f"{len(durs)} 次,共 {sum(durs)/60:.1f} min")
except Exception as e:
v.expect_true(f"{label} chain 有 access", False, str(e)[:60])
v.expect_true("腳本沒有 NACK", nack == 0, f"{ack} ACK / {nack} NACK")
ok = v.report("Week 2 驗證段")
return 0 if ok else 1
if __name__ == "__main__":
_rc = main()
if "ipykernel" not in sys.modules:
raise SystemExit(_rc)