동아리에서 원래 OpenRocket이라는 오픈소스 로켓 시뮬레이션을 사용했습니다. 다만 OpenRocket은 사용하기에는 쉽지만 아무래도 시뮬레이션에 한계가 있어 RocketPy라는 다른 오픈소스 프로젝트를 사용해보려고 합니다.
RocketPy는 매우 정밀한 시뮬레이션이 가능하나 Pytohn이나 matlab에서 코드로 사용하기 때문에 까다롭습니다. 그래서 RocketPy를 엔진으로 사용하는 GUI 프로그램을 만들어보려고 합니다.
바로가기
1. RocketPy의 구성과 실행 순서
RocketPy는 핵심이 되는 다음 네 가지 클래스로 구성됩니다.
Environment: 날씨 데이터 저장Motor: 로켓 엔진((고체/하이브리드/액체로 세분화)) 관련 데이터 저장Rocket: 로켓 관련 데이터 저장Flight: 시뮬레이션 실행 및 결과 저장
그래서 시뮬레이션을 위해 Enviroment, Motor, Rocket 객체를 정의한 뒤, Flight 객체를 정의하여 시뮬레이션을 실행할 수 있습니다. 로켓을 정의하기 위해선 모터가 존재해야 하기 때문에 Motor -> Rocket 순으로 정의하면 됩니다. 고체/하이브리드/액체 엔진 모두 같은 방식을 적용하면 됩니다.
클래스는 일반적인 python 라이브러리 처럼, 다음과 같이 불러올 수 있습니다.
from rocketpy import Environment, SolidMotor, Rocket, Flight
2. 발사 환경 설정 — Environment
발사 위치와 날짜
Environment 클래스에는 발사 장소의 날씨/풍향 데이터를 저장합니다. Environment 객체를 정의하기 위해선 위도, 경도, 고도에 대한 정보가 필요합니다.
# UNIST 위도, 경도, 고도
env = Environment(
latitude=35.5721,
longitude=129.1822,
elevation=53
)
다음으로 GFS(Global Forecast System)와 같은 대기 모델이 필요합니다. 한국에선 LDAPS나 RDAPS로 대체할 수도 있을 듯 합니다. 시뮬레이션 날짜는 (년, 월, 일, 시(UTC 기준)) 형식의 튜플로 정의합니다.
import datetime
tomorrow = datetime.data.today() + datetime.timedelta(days=1)
env.set_data(
(tomorrow.year, tomorrow.month, tomorrow.month, tomorrow.day, hour_in_UTC_time)
)
현재 시간 기준으로 할 거라면 다음과 같이 간단하게 쓸 수 있습니다.
env = Environment(
latitude=35.5721,
longitude=129.1822,
elevation=53,
date=datetime.now() # 현재 시간 기준 예보 다운로드
)
대기 모델과 입력 매개변수
이 이후에 set_atmosperic_model로 사용할 대기 모델을 정합니다. 파라미터는 다음과 같습니다. Return하는 타입은 ’None’이고 발생할 수 있는 에러는 따로 정리하지 않겠습니다.
type(string) : 대기 모델 선택기 (대소문자 구분 없음).- 허용되는 값:
"standard_atmosphere","wyoming_sounding","windy","forecast","reanalysis","ensemble","custom_atmosphere"
- 허용되는 값:
file(string | netCDF4.Dataset, optional) : 데이터 소스 파일 경로, OPeNDAP URL, 또는 모델 단축어. (type에 따라 의미가 달라짐)"standard_atmosphere"및"custom_atmosphere": 무시됨 (사용 안 함)."wyoming_sounding": 와이오밍 대학교 기상 사운딩 관측 텍스트 페이지 웹 URL."windy":"ECMWF","GFS","ICON","ICONEU"중 하나."forecast": 로컬 파일 경로, OPeNDAP URL, 이미 열려 있는netCDF4.Dataset, 또는 최신 기상 예보 자동 다운로드를 위한 단축어("AIGFS","GFS","NAM","RAP","HRRR","HIRESW"중 하나)."reanalysis": 로컬 파일 경로, OPeNDAP URL, 또는 이미 열려 있는netCDF4.Dataset."ensemble": 로컬 파일 경로, OPeNDAP URL, 이미 열려 있는netCDF4.Dataset, 또는 최신 앙상블 예보 다운로드를 위한"GEFS".
dictionary(dict | str, optional) :netCDF4파일 내의 변수명을 표준 이름으로 매핑해 주는 딕셔너리. (type에 따라 의미가 달라짐)"standard_atmosphere","custom_atmosphere","wyoming_sounding": 무시됨."windy": 무시됨."forecast","reanalysis","ensemble": 사용자가 정의한 로컬 매핑 딕셔너리, 또는 파일 구조에 해당하는 내장 매핑 이름("ECMWF","GFS","MERRA2","RAP","HRRR"등) 중 하나.
pressure(float | string | array | callable, optional) : 대기압 프로필을 정의.type="custom_atmosphere"일 때 주로 지정하며, 지정하지 않으면 국제표준대기(ISA) 기압 모델을 사용.float: 고도와 무관한 일정한 상수 기압 정의 (단위:Pa).string: 최대 1줄의 헤더와 2개의 데이터 열을 가진.csv파일 경로. (1열: 해발 기하학적 고도 [m], 2열: 기압 [Pa])array:(고도 [m], 기압 [Pa])좌표 쌍으로 이루어진 리스트 또는 NumPy 2차원 배열.callable(함수): 해발 고도(m)를 유일한 인자로 받아 해당 고도의 기압(Pa)을 반환하는 파이썬 함수.
temperature(float | string | array | callable, optional) : 대기 온도 프로필을 정의.type="custom_atmosphere"일 때 주로 지정하며, 지정하지 않으면 국제표준대기(ISA) 온도 모델을 사용.float: 고도와 무관한 일정한 상수 온도 정의 (단위:K, 켈빈. 예: 20℃ = 293.15K).string: 최대 1줄의 헤더와 2개의 데이터 열을 가진.csv파일 경로. (1열: 해발 기하학적 고도 [m], 2열: 온도 [K])array:(고도 [m], 온도 [K])좌표 쌍으로 이루어진 리스트 또는 NumPy 2차원 배열.callable(함수): 해발 고도(m)를 유일한 인자로 받아 해당 고도의 온도(K)를 반환하는 파이썬 함수.
wind_u(float | string | array | callable, optional) : 동쪽으로 향하는 풍속의 크기(동서 방향 성분, 서풍이 양수)를 정의.type="custom_atmosphere"일 때 지정하며, 지정하지 않으면 0 m/s(무풍)로 간주.float: 고도와 무관한 일정한 상수 풍속 정의 (단위:m/s).string: 최대 1줄의 헤더와 2개의 데이터 열을 가진.csv파일 경로. (1열: 해발 기하학적 고도 [m], 2열: wind-u [m/s])array:(고도 [m], wind-u [m/s])좌표 쌍으로 이루어진 리스트 또는 NumPy 2차원 배열.callable(함수): 해발 고도(m)를 유일한 인자로 받아 해당 고도의 wind-u(m/s)를 반환하는 파이썬 함수.
wind_v(float | string | array | callable, optional) : 북쪽으로 향하는 풍속의 크기(남북 방향 성분, 남풍이 양수)를 정의.type="custom_atmosphere"일 때 지정하며, 지정하지 않으면 0 m/s(무풍)로 간주.float: 고도와 무관한 일정한 상수 풍속 정의 (단위:m/s).string: 최대 1줄의 헤더와 2개의 데이터 열을 가진.csv파일 경로. (1열: 해발 기하학적 고도 [m], 2열: wind-v [m/s])array:(고도 [m], wind-v [m/s])좌표 쌍으로 이루어진 리스트 또는 NumPy 2차원 배열.callable(함수): 해발 고도(m)를 유일한 인자로 받아 해당 고도의 wind-v(m/s)를 반환하는 파이썬 함수.
pressure_conversion_factor(string | int | float, optional) :type이forecast,reanalysis,ensemble일 때 데이터의 기압 단위를Pa로 환산하기 위한 변환 계수.- 외부 기상 데이터 파일의 기압 단위가 파스칼(Pa)이 아닐 수 있어 단위 보정에 필요.
- 유효한 문자열:
"mbar","hPa","Pa". - 사용자 정의 단위 사용 시: 양의 실수(
float) 또는 정수(int). None(기본값): 모델 이름을 바탕으로 자동 감지 (예: ERA5/ECMWF/MERRA2 재해석 파일은 주로hPa, 온라인 GFS/NAM/RAP/HRRR 예보 모델은 주로Pa). 감지할 수 없는 경우 파일 내부의 기압 단위 속성(attribute)을 직접 읽어 처리.
국내 기상 데이터 사용 예시
한국에서는 다음과 같이 사용가능합니다.
# GFS 사용시
env.set_atmospheric_model(type="forecast", file="GFS")
#와이오밍 대학교 데이터 사용시(단, 포항, 오산, 제주 고산, 흑산도 등의 데이터만 존재)
url = "http://weather.uwyo.edu/cgi-bin/sounding?region=seasia&TYPE=TEXT%3ALIST&YEAR=2026&MONTH=10&FROM=0212&TO=0212&STNM=47138"
env.set_atmospheric_model(type="wyoming_sounding", file=url)
# 현장에서 측정/기상청 날씨누리 등
# 예: 지상에서 북서풍 4m/s가 불 때 (남동쪽으로 이동하므로 동(+), 남(-) 성분)
# 4m/s * cos(45°) ≈ 2.8m/s
env.set_atmospheric_model(
type="custom_atmosphere",
wind_u=2.83, # 동쪽 방향 성분 (+)
wind_v=-2.83, # 남쪽 방향 성분 (-)
temperature=293.15, # 20℃
)
`info’ 메서드로 날씨를 데이터 및 플롯으로 확인 할 수도 있습니다.
env.info
3. 고체 모터 정의 — SolidMotor
모터 종류와 물리적 가정
모터는 고체, 액체, 하이브리드가 각각의 클래스(rocketpy.SolidMotor,rocketpy.HybridMotor,rocketpy.LiquidMotor)를 가집니다.
먼저 고체 엔진부터 봐 보겠습니다. 고체 로켓 모터에는 계산을 단순화하기 위한 핵심적인 두 가지 물리적 가정이 있습니다.
- 가운데 구멍 뚫린 원통형 연료(BATES)만 지원한다
- 연소 동안 노즐 가스 분사 속도(비추력)가 일정하다
즉 BATES 기준으로 연료 소모량과 무게 중심 이동을 계산하고, 에서 를 상수로 두고 적분하여 쉽게 질량 변화를 계산한다는 의미인거 같습니다. 자세한건 나중에 코드를 까봐야 알 것 같습니다.
입력 정보와 좌표 기준
고체 모터를 정의하기 위해선 몇가지 정보가 필요합니다.
- 추력원 데이터 (
thrust_source)- 모터의 추력 곡선(시간에 따른 추력 )이 담긴 파일 또는 데이터.
- 지원 형식:
.eng(RASP 포맷),.rse(RockSim 포맷),.csv파일 (또는 단순 실수/함수). - Thrust Source 상세 문서 (이건 나중에 공부)
- 물리적 파라미터 (Physical Parameters)
dry_mass: 연료(추진제)를 제외한 모터 케이스 및 노즐 순수 중량 (kg).dry_inertia: 건조 질량의 회전 관성 모멘트 튜플 ().nozzle_radius: 노즐 최외곽 출구(Exit) 반지름 (m).throat_radius: 노즐 목(Throat, 가장 좁은 부분) 반지름 (m).
- 추진제(그레인) 제원 파라미터 (Propellant Parameters)
grain_number: 연료 덩어리(그레인)의 총 개수.- 그레인 형상: 외경(
grain_outer_radius), 초기 내경(grain_initial_inner_radius), 높이(grain_initial_height), 그레인 간 간격(grain_separation). grain_density: 고체 연료 화약의 밀도 ().
- 위치 좌표 파라미터 (Position Related Parameters)
- 모터 자체의 기준 좌표계(원점)를 기준으로 정의되는 1차원 축 위치:
center_of_dry_mass_position: 모터 껍데기 건조 질량의 무게중심(CoM) 위치.grains_center_of_mass_position: 화약(그레인) 전체의 무게중심(CoM) 위치.nozzle_position: 노즐 출구(바닥)의 위치.
dry_inertia의 I11과 I22는 모터에 수직인 축을 중심으로 한 dry_mass의 관성 모멘트이고 I33은 모터 중심축을 중심으로 한 관성 모멘트입니다. dry_inertia는 좌표계 원점이 아닌 dry_mass 중심을 기준으로 정의됩니다.
모터 생성과 정보 확인
아래처럼 SolidMotor 객체를 생성할 수 있습니다.
# 공식 문서 그대로 가져옴
from rocketpy import SolidMotor
example_solid = SolidMotor(
thrust_source="../data/motors/cesaroni/Cesaroni_M1670.eng",
dry_mass=1.815,
dry_inertia=(0.125, 0.125, 0.002),
nozzle_radius=33 / 1000,
grain_number=5,
grain_density=1815,
grain_outer_radius=33 / 1000,
grain_initial_inner_radius=15 / 1000,
grain_initial_height=120 / 1000,
grain_separation=5 / 1000,
grains_center_of_mass_position=0.397,
center_of_dry_mass_position=0.317,
nozzle_position=0,
burn_time=3.9,
throat_radius=11 / 1000,
coordinate_system_orientation="nozzle_to_combustion_chamber",
)
아래 코드를 이용해서 엔진의 자세한 데이터들을 텍스트와 이미지, 플롯으로 확인할 수 있습니다.
example_solid.all_info()
# 이건 텍스트만
example_solid.info()
하이브리드와 액체 모터는 다음에 공부하고 우선 로켓을 정의하는 방법부터 봐보겠습니다.
4. 로켓 뼈대와 항력 곡선 — Rocket
조립 순서와 기체 정의
Rocket 객체를 완전하게 생성하기 위해선 다음 단계를 진행해야 합니다. 이는 이 문서를 보면 됩니다.
- 로켓 기체의 건조 질량(모터의 건조 질량이 아니라, 모터를 뺀 로켓 전체의 무게), 관성 모멘트(모터를 뺀 로켓의 질량 중심 기준), 항력 계수, 반지름을 입력하여 로켓 뼈대를 정의
- 모터를 추가
- 노즈콘, 날개, 보트테일 등 공력 부품 부착
- 낙하산 사출 장치 설정
- 발사대 레일 버튼 위치 지정
- 조립 완성도, 안정성 검증
로켓이 좌우 비대칭이라면, 뒤틀림 관성까지 6개 숫자로 관성 모멘트를 정밀하게 넣을 수 있습니다. (I11, I22, I33, I12, I13, I23)
from rocketpy import Rocket
calisto = Rocket(
radius=127 / 2000,
mass=14.426,
inertia=(6.321, 6.321, 0.034),
power_off_drag="../data/rockets/calisto/powerOffDragCurve.csv",
power_on_drag="../data/rockets/calisto/powerOnDragCurve.csv",
center_of_mass_without_motor=0,
coordinate_system_orientation="tail_to_nose",
)
모터 작동 전후의 항력 곡선
Rocket 클래스는 엔진이 꺼져 있을 떄와 켜져 있을 때, 두 가지 항력 곡선을 필요로 합니다. 엔진이 켜져 있을 때는 배기가스 때문에 항력 계수가 엔진이 꺼져 있을 떄보다 낮아집니다. 모터가 작동 중일 때의 항력 곡선이 없는 경우, 두 경우에 동일한 항력 곡선을 사용할 수 있습니다. 이 곡선들은 로켓의 항력 계수를 계산하는데 사용되며, CSV 파일의 경로와 마하수가 주어졌을 때 항력 계수를 변환하는 함수, 두 가지 방법으로 정의할 수 있습니다.
CSV파일에 정의된 곡선은 첫 번째 열에 마하수, 두 번째 열에 항력 계수가 있어야 합니다.
0.0, 0.0
0.1, 0.4018816
0.2, 0.38821269
0.3, 0.38150576
0.4, 0.37946785
0.5, 0.38118499
0.6, 0.38947261
0.7, 0.40604949
0.8, 0.40110651
0.9, 0.45696342
1.0, 0.62744566
매우 정확한 항력 곡선을 얻으려면 CFD 소프트웨어나 풍동 데이터를 사용해야 합니다. 이러한 방식을 사용할 수 없다면 RASAero II를 사용할 수 있습니다. 이 프로그램에선 로켓의 형상만 정의하고 _AeroPlots_에 접근하면 됩니다. PyRASAero같은 프로젝트도 존재하는 것 같은데, 이건 나중에 공부해봐야겠습니다.
5. 모터와 공력 부품 추가
모터 연결
로켓의 뼈대를 정의 했으면, 이제 모터를 추가해야 합니다. 이때 position을 좌표계를 생각하여 주의해야 합니다.
calisto.add_motor(example_motor, position=-1.255)
노즈콘·날개·보트테일
다음으론 공력 부품을 추가해야 합니다. 로켓의 공기역학적 힘과 모멘트를 계산하는 데 사용됩니다. 모터와 달리 로켓에 추가하기 전에 미리 정의할 필요가 없고, 한 번에 정의하여 로켓에 추가할 수 있습니다.
nose_cone = calisto.add_nose(
length=0.55829, kind="von karman", position=1.278
)
fin_set = calisto.add_trapezoidal_fins(
n=4,
root_chord=0.120,
tip_chord=0.060,
span=0.110,
position=-1.04956,
cant_angle=0.5,
airfoil=("../data/airfoils/NACA0012-radians.txt","radians"),
)
tail = calisto.add_tail(
top_radius=0.0635, bottom_radius=0.0435, length=0.060, position=-1.194656
)
부품마다 좌표계가 다른데, 이는 여기사 확인 할 수 있습니다.
아래 코드를 통해 완성된 로켓의 형태를 확인할 수 있습니다.
calisto.draw()
날개 단면 데이터 — airfoil
날개 단면(Airdoil)의 형태도 정할 수 있습니다. Rocket.add_trapezolidal_fins와 Rocket.add_elliptical_fins 메서드에는 airfoil 이라는 옵션 매개변수가 존재합니다. 이 매개변수는 None일 수 있고, 이 경우 핀은 평판으로 처리됩니다. None이 아닐 경우, (path, units) 형식의 튜플입니다.
path는 airfoil CSV 파일의 경로이며 첫 번째 열은 받음각, 두 번째 열은 양력 계수입니다.
units는 CSV 파일의 첫 번째 열에 있는 단위입니다. 이 값은 "radians"나 "degrees" 입니다.
0.0, 0.0
0.017453293, 0.11
0.034906585, 0.22
0.052359878, 0.33
0.06981317, 0.44
0.087266463, 0.55
0.104719755, 0.66
0.122173048, 0.746
0.13962634, 0.8274
0.157079633, 0.8527
0.174532925, 0.1325
0.191986218, 0.1095
0.20943951, 0.1533
Airfoil Tools에서 airfoil의 csv 파일을 얻을 수 있습니다.
6. 낙하산과 사출 조건
낙하산 입력값과 생성 예제
다음으로 낙하산을 추가해야 합니다. 이를 위해 다음이 필요합니다.
- 낙하산 항력 계수()와 낙하산 단면적{}을 곱한 값인
cd_s - 낙하산 사출 조건(
trigger) 자세한 사항 - 낙하산 사출 시스템의 센서 감지 주기(
sampling_rate)
선택적으로 다음도 정의할 수 있습니다.
- 낙하산 사출 시스쳄 지연
lag - 낙하산 사출 시스쳄 노이즈
noise
다음은 로켓에 낙하산 두 개를 추가하여 하나는 최고 고도에서, 다른 하나는 지상 800m 상공에서 펼쳐지도록 하는 예제입니다.
main = calisto.add_parachute(
name="Main",
cd_s=10.0,
trigger=800,
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
radius=1.5,
height=1.5,
porosity=0.0432,
)
drogue = calisto.add_parachute(
name="Drogue",
cd_s=1.0,
trigger="apogee",
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
radius=1.5,
height=1.5,
porosity=0.0432,
)
낙하산에 대한 자세한 내용은 rocketpy.Rocket.add_parachute에 있습니다. 이것도 나중에 공부해야 할 것 같습니다.
사출 조건과 사용자 함수 — trigger
낙하산 사출 시점은 매우 중요한 매개변수입닌다. 사출 시점은 숫자, 문자열 "apogee", 또는 호출 가능한 객체입니다.
숫자라면, 낙하산이 펼쳐질 고도이고, "apogee"라면 최고 고도, 호출 가능한 객체라면, 다음 세 개의 매개변수를 받는 함수여야 합니다.
p: 낙하산 noise 신호를 고려한 압력h: 낙하산 noise 신호를 고려한 지면으로부터의 높이y:[x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3]의 상태 벡터
이 함수는 낙하산을 펼쳐야 할 경우 True, 그렇지 않을 경우 False를 반환해야 합니다.
이 함수는 시뮬레이션 전반에 걸쳐 호출되며 따라서 언제든지 이 함수를 이용해 낙하산을 사출할 수 있습니다.
def drogue_trigger(p, h, y):
# activate drogue when vz < 0 m/s.
return True if y[5] < 0 else False
def main_trigger(p, h, y):
# activate main when vz < 0 m/s and z < 800 m
return True if y[5] < 0 and h < 800 else False
실제 로켓에 사용될 C/CPP 코드를 파이썬으로 가져와 이러한 함수로 사용할 수 있습니다. 이를 통해 실제 시스템을 시뮬레이션 할 수 있습니다.
7. 발사대 레일 버튼 설정
다음으로는 레일 버튼을 세팅할 수 있습니다. 레일 버튼은 시뮬레이션에서 선택 사항이지만 현실적인 레일 이탈 속도와 동작을 구현하는 데 중요합니다. Rocketpy에서는 모든 레일 가이드가 레일 버튼으로 시뮬레이션 됩니다. 레일 버튼은 위치에 따라 정의됩니다.
rail_buttons = calisto.set_rail_buttons(
upper_button_position=0.0818,
lower_button_position=-0.618,
angular_position=45,
)
레일 세팅에 관한 자세한 사항은 여기와 여기서 확인 가능합니다.
8. 조립 검증과 회전 특성
정적 여유와 안정성 확인
이제 로켓의 정의는 끝입니다. 로켓에 대한 정보를 그래프로 나타내어 모든 것이 정확한지 확인 할 수 있습니다.
# 정적 여유를 표시하여 로켓이 안정적인지 확인
calisto.plots.static_margin()
# 로켓의 모든 정보 확인(그래프들 포함)
calisto.all_info()
정적 여유(static margin)는 항상 확인해야 합니다. 정적 여유가 음수라면 로켓이 불안정한 상태이며 시뮬레이션이 실패할 가능성이 높고, 지나치게 높으면 로켓이 매우 안정적이어서 시뮬레이션에 실패할 가능성이 높습니다.
매우 안정적이면 시뮬레이션이 오히려 실패할 수 있다는 것이 직관적으로 볼 때 잘 이해가 되지 않았는데, 찾아보니 안정성이 높으면 공기역학적인 복원력이 너무 커져 바람 방향에 의해 진행 방향이 꺾이거나 진동할 수 있다고 합니다.
RocketPy는 이러한 문제를 자동으로 감지하도록 도와줍니다. 모터 점화 시 정적 여유가 음수이면 시뮬레이션에서 로켓을 사용할 때 불안정한 로켓 경고가 발생합니다. 로켓에 GenericSurface 공력 부품이 있는 경우에는 이 검사가 생략됩니다. 이 값의 양력 계수 미분값이 입력 중심 계산에 반영되지 않아 정적 여유가 신뢰할 수 없기 때문입니다. 여기에 다른 오류들도 확인할 수 있습니다. 이건 RockeyPy만 사용할 때는 따로 공부할 필요 없이 발생할 때 찾아보면 되겠지만, RocketPy를 엔진으로 GUI를 만드려는 입장에선 공부를 따로 해야할 것 같습니다.
관성 텐서와 회전 특성
관성 텐서도 알아봅시다. 주어진 시점에서 로켓의 건조 질량 중심을 기준으로 한 관성 텐서는 get_inertia_tensor_at_time 메서드를 사용하여 얻을 수 있습니다. 이 메서드는 지정된 시점에서 관성 텐서의 각 구성 요소를 평가하고 rocketpy.mathutils.Matrix 객체를 반환합니다.
calisto.get_inertia_tensor_at_time(0.5)
get_inertia_tensor_derivation_at_time 메서드를 사용하면 특정 시점에서의 관성 텐서의 미분값을 구할 수도 있습니다.
calisto.get_inertia_tensor_derivative_at_time(0.5)
관성 텐서는 로켓의 대칭성과 회전 용이성에 대한 중요한 정보를 보여줍니다.
관성 텐서(Inertia Tensor)는 로켓의 대칭성과 회전 특성에 대한 중요한 정보를 보여줍니다:
-
축 대칭 (Axisymmetry): 이면 로켓이 원통형으로 균형 잡힌 축 대칭(Axisymmetric) 구조임을 뜻합니다. (피치와 요 방향의 회전 관성이 동일함)
-
관성곱 0 (Zero Products of Inertia): 비대각 성분(관성곱)이 모두 0이면, 기체의 중심축이 회전 주축과 완벽히 일치하여 회전 시 불필요한 뒤틀림이나 쏠림 현상이 발생하지 않음을 뜻합니다.
-
회전 용이성 (Ease of Rotation): 값이 다른 두 값보다 훨씬 작을수록, 로켓이 중심축 회전(롤링, 팽이 회전)은 매우 쉬운 반면, 기체가 옆으로 뒤집히는 회전(피칭/요잉, 텀블링)에는 강하게 저항하여 비행 안정성에 유리합니다.
이 성질들은 좌표계의 원점이 로켓의 무게중심(CoM)에 위치할 때 성립합니다.
이제 시물레이션을 실행할 수 있습니다. 비행을 시뮬레이션 하기 위해 Flight 객체를 생성해야 합니다. 여기부턴 2일차에서 마져 정리하겠습니다.