T-Tail HALE 04:Dynamic response、trajectory 与机身固连变形¶
这一页运行一个短时 DynamicCoupled 响应。这里把老师提到的两个量明确分开:trajectory 看刚体参考系的真实运动,deformation 在机身固连坐标系下看弹性变形。
0. 导入公共工具¶
In [1]:
Copied!
import os
import sys
from pathlib import Path
os.environ.setdefault("MPLCONFIGDIR", str(Path("_sharpy_runs") / ".mplconfig"))
candidate_dirs = [
Path.cwd(),
Path.cwd() / "docs" / "reference-aircraft",
Path("docs/reference-aircraft"),
]
for candidate in candidate_dirs:
if (candidate / "ttail_hale_utils.py").exists():
sys.path.insert(0, str(candidate.resolve()))
break
else:
raise FileNotFoundError("没有找到 ttail_hale_utils.py。请从仓库根目录或 docs/reference-aircraft 目录运行本 Notebook。")
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML, display
import ttail_hale_utils as hale
hale.configure_plots()
import os
import sys
from pathlib import Path
os.environ.setdefault("MPLCONFIGDIR", str(Path("_sharpy_runs") / ".mplconfig"))
candidate_dirs = [
Path.cwd(),
Path.cwd() / "docs" / "reference-aircraft",
Path("docs/reference-aircraft"),
]
for candidate in candidate_dirs:
if (candidate / "ttail_hale_utils.py").exists():
sys.path.insert(0, str(candidate.resolve()))
break
else:
raise FileNotFoundError("没有找到 ttail_hale_utils.py。请从仓库根目录或 docs/reference-aircraft 目录运行本 Notebook。")
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML, display
import ttail_hale_utils as hale
hale.configure_plots()
1. 写入短时动态配置¶
这里使用一个 1-cos gust 作为扰动。为了让动态可视化更明显,默认跑 80 个 time step;如果只是做快速 smoke test,可以把 DYNAMIC_STEPS 改回 32。
In [2]:
Copied!
case_dir = hale.prepare_hale_case("04_dynamic_response", force=True)
DYNAMIC_STEPS = 80
DT = 0.025
dynamic_config = hale.write_solver_variant(
case_dir,
"ttail_hale_short_dynamic.sharpy",
["BeamLoader", "AerogridLoader", "StaticTrim", "BeamLoads", "DynamicCoupled"],
n_steps=DYNAMIC_STEPS,
)
print("dynamic config:", dynamic_config)
print("time horizon [s]:", DYNAMIC_STEPS * DT)
case_dir = hale.prepare_hale_case("04_dynamic_response", force=True)
DYNAMIC_STEPS = 80
DT = 0.025
dynamic_config = hale.write_solver_variant(
case_dir,
"ttail_hale_short_dynamic.sharpy",
["BeamLoader", "AerogridLoader", "StaticTrim", "BeamLoads", "DynamicCoupled"],
n_steps=DYNAMIC_STEPS,
)
print("dynamic config:", dynamic_config)
print("time horizon [s]:", DYNAMIC_STEPS * DT)
dynamic config: /Users/raiot/Documents/Programing/Steps-to-Aeroelastic-Sim/_sharpy_runs/ttail_hale_reference_aircraft/04_dynamic_response/simple_HALE/ttail_hale_short_dynamic.sharpy time horizon [s]: 2.0
2. 运行短时动态响应¶
In [3]:
Copied!
dynamic_data, dynamic_log = hale.run_solver(dynamic_config)
print("structure timesteps:", len(dynamic_data.structure.timestep_info))
print("aero timesteps:", len(dynamic_data.aero.timestep_info))
print("captured solver messages:", len(dynamic_log))
dynamic_data, dynamic_log = hale.run_solver(dynamic_config)
print("structure timesteps:", len(dynamic_data.structure.timestep_info))
print("aero timesteps:", len(dynamic_data.aero.timestep_info))
print("captured solver messages:", len(dynamic_log))
structure timesteps: 81 aero timesteps: 81 captured solver messages: 2
3. Physical trajectory:参考系真实运动¶
在 SHARPy 的动态结果里,结构节点 pos 更接近机体坐标下的弹性形状;飞行轨迹要从 structural timestep 的 for_pos、for_vel 和姿态角里读。下面这组图回答的是:飞机整体沿什么路径飞、速度和姿态如何变化。
In [4]:
Copied!
trajectory_rows = hale.trajectory_rows(dynamic_data, dt=DT)
display(HTML(hale.html_table(trajectory_rows[:5], precision=5)))
display(HTML(hale.html_table(trajectory_rows[-5:], precision=5)))
hale.plot_trajectory(trajectory_rows)
hale.show_plotly(
[hale.trajectory_trace(trajectory_rows)],
"Physical trajectory of the structural frame of reference",
height=520,
camera={"eye": {"x": 1.4, "y": -1.65, "z": 0.85}},
)
trajectory_rows = hale.trajectory_rows(dynamic_data, dt=DT)
display(HTML(hale.html_table(trajectory_rows[:5], precision=5)))
display(HTML(hale.html_table(trajectory_rows[-5:], precision=5)))
hale.plot_trajectory(trajectory_rows)
hale.show_plotly(
[hale.trajectory_trace(trajectory_rows)],
"Physical trajectory of the structural frame of reference",
height=520,
camera={"eye": {"x": 1.4, "y": -1.65, "z": 0.85}},
)
| time [s] | FoR x [m] | FoR y [m] | FoR z [m] | u [m/s] | v [m/s] | w [m/s] | roll [deg] | pitch [deg] | yaw [deg] |
|---|---|---|---|---|---|---|---|---|---|
| 0.00000 | 0.00000 | 0.00000 | 0.00000 | -9.97180 | 0.00000 | -0.75047 | 0.00000 | 4.30390 | 0.00000 |
| 0.02500 | -0.25000 | -0.00000 | -0.00000 | -9.97179 | -0.00000 | -0.75048 | -0.00000 | 4.30390 | -0.00000 |
| 0.05000 | -0.50000 | -0.00000 | -0.00000 | -9.97175 | -0.00000 | -0.75054 | -0.00000 | 4.30390 | -0.00000 |
| 0.07500 | -0.75000 | -0.00000 | -0.00001 | -9.97168 | -0.00000 | -0.75067 | -0.00000 | 4.30388 | -0.00000 |
| 0.10000 | -0.99999 | -0.00000 | -0.00002 | -9.97165 | 0.00000 | -0.75084 | -0.00000 | 4.30384 | -0.00000 |
| time [s] | FoR x [m] | FoR y [m] | FoR z [m] | u [m/s] | v [m/s] | w [m/s] | roll [deg] | pitch [deg] | yaw [deg] |
|---|---|---|---|---|---|---|---|---|---|
| 1.90000 | -19.03512 | 0.00000 | 1.59427 | -9.56040 | 0.00000 | -0.68993 | 0.00000 | 8.79307 | 0.00000 |
| 1.92500 | -19.27333 | 0.00000 | 1.61306 | -9.53390 | 0.00000 | -0.68104 | 0.00000 | 8.59766 | 0.00000 |
| 1.95000 | -19.51094 | 0.00000 | 1.63118 | -9.50818 | 0.00000 | -0.66943 | 0.00000 | 8.38670 | 0.00000 |
| 1.97500 | -19.74795 | 0.00000 | 1.64861 | -9.48345 | 0.00000 | -0.65594 | 0.00000 | 8.16249 | 0.00000 |
| 2.00000 | -19.98440 | 0.00000 | 1.66534 | -9.45990 | 0.00000 | -0.64148 | 0.00000 | 7.92741 | 0.00000 |
4. Fuselage-fixed deformation:机身固连只看变形¶
这一节把机身梁作为参考:机鼻附近节点作为原点,机身梁指向 x_body,垂尾给出 z_body。这样可以去掉整体飞行轨迹和姿态变化,只看机翼、尾翼相对机身的弹性变形。
In [5]:
Copied!
deformation_rows = hale.deformation_response_rows(dynamic_data, dt=DT)
display(HTML(hale.html_table(deformation_rows[:5], precision=5)))
display(HTML(hale.html_table(deformation_rows[-5:], precision=5)))
hale.plot_deformation_response(deformation_rows)
deformation_rows = hale.deformation_response_rows(dynamic_data, dt=DT)
display(HTML(hale.html_table(deformation_rows[:5], precision=5)))
display(HTML(hale.html_table(deformation_rows[-5:], precision=5)))
hale.plot_deformation_response(deformation_rows)
| time [s] | right wing tip dz_body [m] | left wing tip dz_body [m] | tail top dz_body [m] | sum Fz |
|---|---|---|---|---|
| 0.00000 | 0.00000 | 0.00000 | 0.00000 | 50.14381 |
| 0.02500 | -0.00005 | -0.00005 | -0.00005 | 46.11130 |
| 0.05000 | -0.00023 | -0.00023 | -0.00023 | 47.62102 |
| 0.07500 | -0.00055 | -0.00055 | -0.00055 | 48.92637 |
| 0.10000 | -0.00091 | -0.00091 | -0.00091 | 50.02166 |
| time [s] | right wing tip dz_body [m] | left wing tip dz_body [m] | tail top dz_body [m] | sum Fz |
|---|---|---|---|---|
| 1.90000 | 0.06770 | 0.06770 | 0.06770 | -10.92844 |
| 1.92500 | 0.06617 | 0.06617 | 0.06617 | -13.76486 |
| 1.95000 | 0.06479 | 0.06479 | 0.06479 | -18.51346 |
| 1.97500 | 0.06345 | 0.06345 | 0.06345 | -24.94766 |
| 2.00000 | 0.06201 | 0.06201 | 0.06201 | -32.87639 |
5. 机身固连三维变形叠加¶
下面把初始、中间和最终时间步叠在一张三维图里。因为坐标系已经跟随机身,这张图强调的是结构相对机身的 deformation,而不是飞机整体 trajectory。
In [6]:
Copied!
timesteps = [0, len(dynamic_data.structure.timestep_info) // 2, len(dynamic_data.structure.timestep_info) - 1]
traces = hale.overlay_deformation_traces(dynamic_data, timesteps, deformation_scale=1.0)
hale.show_plotly(
traces,
"Fuselage-fixed deformation overlay: initial, middle and final states",
camera={"eye": {"x": 1.55, "y": -1.9, "z": 0.85}},
)
timesteps = [0, len(dynamic_data.structure.timestep_info) // 2, len(dynamic_data.structure.timestep_info) - 1]
traces = hale.overlay_deformation_traces(dynamic_data, timesteps, deformation_scale=1.0)
hale.show_plotly(
traces,
"Fuselage-fixed deformation overlay: initial, middle and final states",
camera={"eye": {"x": 1.55, "y": -1.9, "z": 0.85}},
)
6. 输出文件位置¶
如果要在 ParaView 里看完整时序,SHARPy 的 BeamPlot / AerogridPlot 会把 .vtp 和 .vtu 写到 output/simple_HALE/ 下面。notebook 中的 Plotly 图则直接来自内存里的 dynamic_data,不依赖 ParaView。
In [7]:
Copied!
output_files = sorted((case_dir / "output" / hale.CASE_NAME).rglob("*.vt*"))
print("VTK-like files:", len(output_files))
for path in output_files[:8]:
print(path.relative_to(case_dir))
output_files = sorted((case_dir / "output" / hale.CASE_NAME).rglob("*.vt*"))
print("VTK-like files:", len(output_files))
for path in output_files[:8]:
print(path.relative_to(case_dir))
VTK-like files: 880 output/simple_HALE/aero/body_simple_HALE_00_000001.vtu output/simple_HALE/aero/body_simple_HALE_00_000002.vtu output/simple_HALE/aero/body_simple_HALE_00_000003.vtu output/simple_HALE/aero/body_simple_HALE_00_000004.vtu output/simple_HALE/aero/body_simple_HALE_00_000005.vtu output/simple_HALE/aero/body_simple_HALE_00_000006.vtu output/simple_HALE/aero/body_simple_HALE_00_000007.vtu output/simple_HALE/aero/body_simple_HALE_00_000008.vtu