ユーザ用ツール

サイト用ツール


ruby:curses-gravity

万有引力の数値計算

ロケットの軌道計算

地球上からロケットを発射すると、ロケットは地球の引力の影響を受けて運動する。 宇宙空間は3次元空間だが、地球の引力による運動では2次元平面上で考えることができる。


Ruby プログラム

プログラム orbit_rocket.rb は、地球上から発射した推進力のないロケット(SpinLaunch)がどのように運動するかを Curses で端末上に表示する。 初期値によってロケットの軌道が変化する。

初期値

  • $(x_{0},y_{0})$:ロケットの発射位置(単位 m(メートル))
  • $(u_{0},v_{0})$:ロケットの x 方向と y 方向の発射速度(単位 m/s(メートル毎秒))
  • $dt$:時間の刻み(単位 s(秒))

ロケットの初期値

orbit_rocket.rb
  1. #!/usr/koeki/bin/ruby
  2. # -*- coding: utf-8 -*-
  3. # Curses でロケットの運動を描画する
  4. # 作者:山本裕樹
  5.  
  6. require "curses"
  7.  
  8. # 万有引力定数 G = 6.67430×10^-11 [m^3/kg/s^2]
  9. # 地球の質量 ME = 5.9724×10^24 [kg]
  10. # 地球の半径 RE = 6.3781x10^6 [m]
  11. $G = 6.67430e-11
  12. $ME = 5.9724e24
  13. $RE = 6.3781e6
  14.  
  15. # 現実の座標から端末上の座標に変換するための x, y 方向のスケール
  16. $xscale = 10.0
  17. $yscale = 5.0
  18.  
  19. # 初期値
  20. # ($x0, $y0):ロケットの発射位置(単位 [m])
  21. # ($u0, $v0):ロケットの発射速度(単位 [m/s])
  22. # $dt:時間の刻み(単位 [s])
  23. $x0 = $RE + 10
  24. $y0 = 0
  25. $u0 = 6000
  26. $v0 = 6000
  27. $dt = 60
  28.  
  29. # 原点から (x, y) までの距離
  30. def r(x, y)
  31. return Math.sqrt(x ** 2 + y ** 2)
  32. end
  33.  
  34. # 運動方程式の右辺1
  35. def f(x, y)
  36. return - $G * $ME * x / r(x, y) ** 3
  37. end
  38.  
  39. # 運動方程式の右辺2
  40. def g(x, y)
  41. return - $G * $ME * y / r(x, y) ** 3
  42. end
  43.  
  44. # ku, kv, kx, ky の計算
  45. def calc_k(u, v, x, y, du, dv, dx, dy)
  46. return f(x + dx, y + dy) * $dt,
  47. g(x + dx, y + dy) * $dt,
  48. (u + du) * $dt,
  49. (v + dv) * $dt
  50. end
  51.  
  52. # ルンゲ=クッタ法で $dt 後の (x,y) と (u,v) を求める
  53. def next_step(u, v, x, y)
  54. ku1, kv1, kx1, ky1 = calc_k(u, v, x, y, 0, 0, 0, 0)
  55. ku2, kv2, kx2, ky2 = calc_k(u, v, x, y,
  56. ku1 * 0.5, kv1 * 0.5, kx1 * 0.5, ky1 * 0.5)
  57. ku3, kv3, kx3, ky3 = calc_k(u, v, x, y,
  58. ku2 * 0.5, kv2 * 0.5, kx2 * 0.5, ky2 * 0.5)
  59. ku4, kv4, kx4, ky4 = calc_k(u, v, x, y, ku3, kv3, kx3, ky3)
  60. return u + (ku1 + 2 * ku2 + 2 * ku3 + ku4) / 6.0,
  61. v + (kv1 + 2 * kv2 + 2 * kv3 + kv4) / 6.0,
  62. x + (kx1 + 2 * kx2 + 2 * kx3 + kx4) / 6.0,
  63. y + (ky1 + 2 * ky2 + 2 * ky3 + ky4) / 6.0
  64. end
  65.  
  66. # 現実の座標 (x,y) から端末上の座標 (cx,cy) へ変換
  67. def x2cx(x) ($cox + x / $RE * $xscale).round end
  68. def y2cy(y) ($coy - y / $RE * $yscale).round end
  69.  
  70. # 端末上の座標 (cx,cy) から現実の座標 (x,y) へ変換
  71. def cx2x(cx) (cx - $cox) * $RE / $xscale end
  72. def cy2y(cy) -(cy - $coy) * $RE / $yscale end
  73.  
  74. # (0, 0) を中心に地球を描く
  75. def draw_earth
  76. # 地球の色の設定
  77. Curses.attrset(Curses.color_pair(2))
  78.  
  79. # 端末上の全ての座標をスキャンして地球の半径内なら地球を出力
  80. 0.upto(Curses.cols - 1) do |cx|
  81. 0.upto(Curses.lines - 1) do |cy|
  82. if r(cx2x(cx), cy2y(cy)) < $RE
  83. Curses.setpos(cy, cx)
  84. Curses.addch(' ')
  85. end
  86. end
  87. end
  88. end
  89.  
  90. # 現実の座標 (x,y) から端末上の座標へ変換して文字 ch を出力
  91. def puts_char(x, y, ch)
  92. cx = x2cx(x)
  93. cy = y2cy(y)
  94. if (cx >= 0 && cx <= Curses.cols - 1) &&
  95. (cy >= 0 && cy <= Curses.lines - 1)
  96. Curses.setpos(cy, cx)
  97. Curses.addch(ch)
  98. end
  99. end
  100.  
  101. # Cursesの初期化
  102. Curses.init_screen
  103.  
  104. begin
  105. # 色の設定
  106. Curses.start_color
  107. Curses.init_pair(1, Curses::COLOR_WHITE, Curses::COLOR_BLACK)
  108. Curses.init_pair(2, Curses::COLOR_BLACK, Curses::COLOR_BLUE)
  109. Curses.init_pair(3, Curses::COLOR_CYAN, Curses::COLOR_BLACK)
  110. Curses.init_pair(4, Curses::COLOR_BLACK, Curses::COLOR_RED)
  111.  
  112. # 端末の中央の座標
  113. $cox = ((Curses.cols - 1) * 0.5).round + 0.5
  114. $coy = ((Curses.lines - 1) * 0.5).round + 0.5
  115.  
  116. # カーソルを非表示
  117. Curses.curs_set(0)
  118.  
  119. # キー入力で待つ時間(ミリ秒)
  120. Curses.stdscr.timeout = 60
  121.  
  122. # 初期値を代入
  123. x = $x0
  124. y = $y0
  125. u = $u0
  126. v = $v0
  127.  
  128. # 時間
  129. i = 0
  130.  
  131. # ロケットの軌道を描くループ
  132. while 1
  133. # 地球を出力
  134. draw_earth
  135.  
  136. # デフォルトの色
  137. Curses.attrset(Curses.color_pair(1))
  138.  
  139. # 時刻の出力
  140. Curses.setpos(Curses.lines - 1, 0)
  141. Curses.addstr(sprintf("t=%d[s]\n", i * $dt))
  142.  
  143. if (r(x, y) > $RE)
  144. # ロケットを出力
  145. puts_char(x, y, 'R')
  146. else
  147. # 爆発を出力
  148. Curses.attrset(Curses.color_pair(4))
  149. puts_char(x, y, 'X')
  150. Curses.refresh
  151. # ループを抜ける
  152. break
  153. end
  154.  
  155. # キー入力があるか
  156. if Curses.getch != nil
  157. # ループを抜ける
  158. break
  159. end
  160.  
  161. # (x, y) にロケットの跡を出力
  162. Curses.attrset(Curses.color_pair(3))
  163. puts_char(x, y, '.')
  164.  
  165. # 画面表示の更新
  166. Curses.refresh
  167.  
  168. # $dt 後の (x,y) を求める
  169. u, v, x, y = next_step(u, v, x, y)
  170. i += 1
  171. end
  172.  
  173. # キー入力があるまでずっと待つ
  174. Curses.stdscr.timeout = -1
  175. Curses.getch
  176.  
  177. ensure
  178. # Cursesの終了処理
  179. Curses.close_screen
  180. end

ロケットの発射位置は地上から 10m の高さに設定している(23行目、24行目)。

ロケットの発射速度 $(u_{0},v_{0})$ を色々変えてみよう(25行目、26行目)。

No. $u_{0}$ [m/s] $v_{0}$ [m/s] 結果
1 6000 6000 xy方向に発射されたロケットはしばらくは飛ぶが最後は地球に落下する。
2 0 7906 y方向に発射されたロケットは地上すれすれを周り続ける。この速度を第一宇宙速度という。
3 0 10000 y方向に発射されたロケットは地球の周りを回り続ける。
4 0 11180 y方向に発射されたロケットは地球の引力を振り切って戻ってこない。この速度を第二宇宙速度という。

理論

地球の中心を原点としたロケットの座標を $(x,y)$ とする。 ロケットは地球の引力を受けて運動するため、$(x,y)$ は時間 $t$ で変化する。

ロケットの速度を $(u,v)$ とすると、$(x,y)$ と以下の関係にある。 \begin{eqnarray*} \frac{dx}{dt}&=&u\\ \frac{dy}{dt}&=&v \end{eqnarray*} ここで $\frac{dx}{dt}$ と $\frac{dy}{dt}$ は $x$ と $y$ をそれぞれ時間 $t$ で微分することを意味する。

ロケットは地球の引力を受けて運動するため、万有引力の法則から次の運動方程式が成り立つ。 \begin{eqnarray*} m\frac{du}{dt}&=&-\frac{GM_{\mathrm{E}}m}{r^{3}}x\\ m\frac{dv}{dt}&=&-\frac{GM_{\mathrm{E}}m}{r^{3}}y \end{eqnarray*} ここで $G$ は万有引力定数、$M_{\mathrm{E}}$ は地球の質量、$m$ はロケットの質量である。 \[ G=6.67430\times 10^{-11}\mathrm{[m^{3}kg^{-1}s^{-2}]} \] \[ M_{\mathrm{E}}=5.9724\times 10^{24}\mathrm{[kg]} \] 両辺を $m$ で割ると $m$ は消えるため、この計算にロケットの質量は不要である。 \begin{eqnarray*} \frac{du}{dt}&=&-\frac{GM_{\mathrm{E}}}{r^{3}}x\\ \frac{dv}{dt}&=&-\frac{GM_{\mathrm{E}}}{r^{3}}y \end{eqnarray*} $r$ は地球の中心からロケットまでの距離で、三平方の定理(ピタゴラスの定理)から次の式で求められる。 \[ r=\sqrt{x^{2}+y^{2}} \]

以上の式を解くとロケットの座標 $(x,y)$ が時間 $t$ とともにどのように変化するか、ロケットの軌道を求めることができる。


数値計算

以下の4つの方程式(微分方程式という)を解くとロケットの運動の軌道が求められる。 \begin{eqnarray*} \frac{dx}{dt}&=&u\\ \frac{dy}{dt}&=&v\\ \frac{du}{dt}&=&f(x,y)\\ \frac{dv}{dt}&=&g(x,y) \end{eqnarray*} ここで \begin{eqnarray*} f(x,y)&=&-\frac{GM_{\mathrm{E}}}{r^{3}}x\\ g(x,y)&=&-\frac{GM_{\mathrm{E}}}{r^{3}}y \end{eqnarray*} とした。

微分方程式はオイラー法やルンゲ=クッタ法などを用いて数値的に計算できる。

ruby/curses-gravity.txt · 最終更新: 2023/06/21 04:23 by yuki